🏗️ 架构设计模式
lzt 项目采用现代软件架构设计模式,确保代码的可扩展性、可维护性和可测试性。
🎯 架构设计原则
SOLID 原则
DDD 领域驱动设计
go
// 领域层 - Domain Layer
type Transaction struct {
ID TransactionID
LedgerID LedgerID
Amount Money
Type TransactionType
Description string
CreatedAt time.Time
}
// 领域服务 - Domain Service
type TransactionDomainService struct {
// 纯业务逻辑,不依赖基础设施
}
func (s *TransactionDomainService) ValidateTransaction(tx *Transaction) error {
if tx.Amount.Amount <= 0 {
return errors.New("amount must be positive")
}
if tx.Type == "" {
return errors.New("transaction type is required")
}
return nil
}
// 仓储接口 - Repository Interface (在领域层定义)
type TransactionRepository interface {
Create(ctx context.Context, tx *Transaction) error
GetByID(ctx context.Context, id TransactionID) (*Transaction, error)
List(ctx context.Context, filter ListFilter) ([]*Transaction, error)
}🏛️ 分层架构
Clean Architecture 实现
┌─────────────────────────────────────────────┐
│ UI Layer │
│ (cmd/, HTTP handlers) │
├─────────────────────────────────────────────┤
│ Application Layer │
│ (services, use cases) │
├─────────────────────────────────────────────┤
│ Domain Layer │
│ (entities, domain services) │
├─────────────────────────────────────────────┤
│ Infrastructure Layer │
│ (repositories, external APIs) │
└─────────────────────────────────────────────┘实际代码结构
go
// cmd/ledger/main.go - UI Layer
func main() {
cfg := config.Load()
// 依赖注入
container := wire.NewContainer()
app := container.GetLedgerApplication()
// 启动服务
server := http.NewServer(app)
server.Start()
}
// internal/app/ledger/application/service.go - Application Layer
type TransactionApplicationService struct {
repo domain.TransactionRepository // 依赖领域层接口
domainSvc *domain.TransactionDomainService
eventBus EventBus
}
func (s *TransactionApplicationService) CreateTransaction(ctx context.Context, cmd CreateTransactionCommand) (*TransactionDTO, error) {
// 1. 命令验证
if err := s.validateCommand(cmd); err != nil {
return nil, err
}
// 2. 构建领域对象
tx := &domain.Transaction{
ID: domain.NewTransactionID(),
LedgerID: domain.LedgerID(cmd.LedgerID),
Amount: domain.Money{Amount: cmd.Amount, Currency: cmd.Currency},
Type: domain.TransactionType(cmd.Type),
Description: cmd.Description,
CreatedAt: time.Now(),
}
// 3. 领域验证
if err := s.domainSvc.ValidateTransaction(tx); err != nil {
return nil, fmt.Errorf("domain validation failed: %w", err)
}
// 4. 持久化
if err := s.repo.Create(ctx, tx); err != nil {
return nil, fmt.Errorf("create transaction failed: %w", err)
}
// 5. 发布事件
event := TransactionCreatedEvent{
TransactionID: tx.ID,
LedgerID: tx.LedgerID,
Amount: tx.Amount,
CreatedAt: tx.CreatedAt,
}
s.eventBus.Publish(event)
// 6. 返回 DTO
return s.toDTO(tx), nil
}
// internal/app/ledger/domain/transaction.go - Domain Layer
package domain
type Transaction struct {
id TransactionID
ledgerID LedgerID
amount Money
txType TransactionType
description string
createdAt time.Time
}
// 领域方法
func (t *Transaction) ChangeDescription(newDesc string) error {
if len(newDesc) > 500 {
return errors.New("description too long")
}
t.description = newDesc
return nil
}
// internal/pkg/database/transaction_repository.go - Infrastructure Layer
type GormTransactionRepository struct {
db *gorm.DB
}
func (r *GormTransactionRepository) Create(ctx context.Context, tx *domain.Transaction) error {
model := r.toModel(tx)
return r.db.WithContext(ctx).Create(model).Error
}🎭 设计模式应用
1. 建造者模式 (Builder Pattern)
go
// 复杂对象构建
type TransactionBuilder struct {
transaction *Transaction
}
func NewTransactionBuilder() *TransactionBuilder {
return &TransactionBuilder{
transaction: &Transaction{
ID: generateID(),
CreatedAt: time.Now(),
},
}
}
func (b *TransactionBuilder) WithLedger(ledgerID string) *TransactionBuilder {
b.transaction.LedgerID = ledgerID
return b
}
func (b *TransactionBuilder) WithAmount(amount int64, currency string) *TransactionBuilder {
b.transaction.Amount = Money{Amount: amount, Currency: currency}
return b
}
func (b *TransactionBuilder) WithType(txType TransactionType) *TransactionBuilder {
b.transaction.Type = txType
return b
}
func (b *TransactionBuilder) WithDescription(desc string) *TransactionBuilder {
b.transaction.Description = desc
return b
}
func (b *TransactionBuilder) Build() (*Transaction, error) {
if err := b.validate(); err != nil {
return nil, err
}
return b.transaction, nil
}
// 使用示例
tx, err := NewTransactionBuilder().
WithLedger("ledger-123").
WithAmount(1000, "CNY").
WithType(TransactionTypeExpense).
WithDescription("午餐费用").
Build()2. 策略模式 (Strategy Pattern)
go
// 不同的处理策略
type ProcessingStrategy interface {
Process(data interface{}) error
CanHandle(dataType string) bool
}
// 并发处理策略
type ConcurrentProcessingStrategy struct {
workers int
}
func (s *ConcurrentProcessingStrategy) Process(data interface{}) error {
items := data.([]interface{})
return bubble.ProcessConcurrent(items, s.processItem, bubble.Options{
Workers: s.workers,
})
}
func (s *ConcurrentProcessingStrategy) CanHandle(dataType string) bool {
return dataType == "large_dataset"
}
// 顺序处理策略
type SequentialProcessingStrategy struct{}
func (s *SequentialProcessingStrategy) Process(data interface{}) error {
items := data.([]interface{})
return bubble.ProcessSequential(items, s.processItem)
}
func (s *SequentialProcessingStrategy) CanHandle(dataType string) bool {
return dataType == "small_dataset"
}
// 处理器上下文
type ProcessorContext struct {
strategies []ProcessingStrategy
}
func (c *ProcessorContext) Process(data interface{}, dataType string) error {
for _, strategy := range c.strategies {
if strategy.CanHandle(dataType) {
return strategy.Process(data)
}
}
return errors.New("no suitable strategy found")
}3. 观察者模式 (Observer Pattern)
go
// 事件总线实现
type EventBus interface {
Subscribe(eventType string, handler EventHandler)
Unsubscribe(eventType string, handler EventHandler)
Publish(event Event)
}
type EventHandler interface {
Handle(event Event) error
}
type Event interface {
Type() string
Data() interface{}
}
// 具体事件
type TransactionCreatedEvent struct {
TransactionID string
LedgerID string
Amount Money
CreatedAt time.Time
}
func (e TransactionCreatedEvent) Type() string {
return "transaction.created"
}
func (e TransactionCreatedEvent) Data() interface{} {
return e
}
// 事件处理器
type NotificationHandler struct {
emailService EmailService
}
func (h *NotificationHandler) Handle(event Event) error {
if event.Type() == "transaction.created" {
data := event.Data().(TransactionCreatedEvent)
if data.Amount.Amount > 10000 { // 大额交易通知
return h.emailService.SendNotification(
"大额交易提醒",
fmt.Sprintf("交易金额: %d %s", data.Amount.Amount, data.Amount.Currency),
)
}
}
return nil
}
// 统计处理器
type StatisticsHandler struct {
statsRepo StatisticsRepository
}
func (h *StatisticsHandler) Handle(event Event) error {
if event.Type() == "transaction.created" {
data := event.Data().(TransactionCreatedEvent)
return h.statsRepo.UpdateLedgerStats(data.LedgerID, data.Amount)
}
return nil
}
// 使用示例
func setupEventHandlers(eventBus EventBus) {
notificationHandler := &NotificationHandler{emailService: emailSvc}
statisticsHandler := &StatisticsHandler{statsRepo: statsRepo}
eventBus.Subscribe("transaction.created", notificationHandler)
eventBus.Subscribe("transaction.created", statisticsHandler)
}4. 工厂模式 (Factory Pattern)
go
// 抽象工厂
type RepositoryFactory interface {
CreateTransactionRepository() TransactionRepository
CreateLedgerRepository() LedgerRepository
CreateBudgetRepository() BudgetRepository
}
// MySQL 工厂实现
type MySQLRepositoryFactory struct {
db *gorm.DB
}
func (f *MySQLRepositoryFactory) CreateTransactionRepository() TransactionRepository {
return &MySQLTransactionRepository{db: f.db}
}
func (f *MySQLRepositoryFactory) CreateLedgerRepository() LedgerRepository {
return &MySQLLedgerRepository{db: f.db}
}
func (f *MySQLRepositoryFactory) CreateBudgetRepository() BudgetRepository {
return &MySQLBudgetRepository{db: f.db}
}
// Memory 工厂实现
type MemoryRepositoryFactory struct {
storage *MemoryStorage
}
func (f *MemoryRepositoryFactory) CreateTransactionRepository() TransactionRepository {
return &MemoryTransactionRepository{storage: f.storage}
}
// 工厂方法
func NewRepositoryFactory(driverType string, config *Config) (RepositoryFactory, error) {
switch driverType {
case "mysql":
db, err := setupMySQLConnection(config)
if err != nil {
return nil, err
}
return &MySQLRepositoryFactory{db: db}, nil
case "memory":
storage := NewMemoryStorage()
return &MemoryRepositoryFactory{storage: storage}, nil
default:
return nil, fmt.Errorf("unsupported driver type: %s", driverType)
}
}5. 命令模式 (Command Pattern)
go
// 命令接口
type Command interface {
Execute() error
Undo() error
GetDescription() string
}
// 具体命令
type CreateTransactionCommand struct {
service *TransactionService
request *CreateTransactionRequest
createdTx *Transaction
executed bool
}
func (c *CreateTransactionCommand) Execute() error {
if c.executed {
return errors.New("command already executed")
}
tx, err := c.service.CreateTransaction(context.Background(), c.request)
if err != nil {
return err
}
c.createdTx = tx
c.executed = true
return nil
}
func (c *CreateTransactionCommand) Undo() error {
if !c.executed || c.createdTx == nil {
return errors.New("cannot undo: command not executed")
}
err := c.service.DeleteTransaction(context.Background(), c.createdTx.ID)
if err != nil {
return err
}
c.executed = false
c.createdTx = nil
return nil
}
func (c *CreateTransactionCommand) GetDescription() string {
return fmt.Sprintf("Create transaction for ledger %s", c.request.LedgerID)
}
// 命令调用器
type CommandInvoker struct {
commands []Command
current int
}
func (i *CommandInvoker) Execute(cmd Command) error {
if err := cmd.Execute(); err != nil {
return err
}
// 清除撤销历史,添加新命令
i.commands = i.commands[:i.current]
i.commands = append(i.commands, cmd)
i.current = len(i.commands)
return nil
}
func (i *CommandInvoker) Undo() error {
if i.current <= 0 {
return errors.New("no commands to undo")
}
i.current--
return i.commands[i.current].Undo()
}
func (i *CommandInvoker) Redo() error {
if i.current >= len(i.commands) {
return errors.New("no commands to redo")
}
err := i.commands[i.current].Execute()
if err != nil {
return err
}
i.current++
return nil
}🔧 依赖注入
Wire 自动注入
go
//go:build wireinject
// +build wireinject
package main
import (
"github.com/google/wire"
"lzt/internal/app/ledger"
"lzt/internal/pkg/config"
"lzt/internal/pkg/database"
)
// 提供者集合
var DatabaseSet = wire.NewSet(
database.NewConnection,
database.NewTransactionRepository,
database.NewLedgerRepository,
)
var ServiceSet = wire.NewSet(
ledger.NewTransactionService,
ledger.NewLedgerService,
ledger.NewBudgetService,
)
var ApplicationSet = wire.NewSet(
ledger.NewTransactionApplicationService,
ledger.NewLedgerApplicationService,
)
// 构建函数
func InitializeLedgerApplication() (*ledger.Application, error) {
wire.Build(
config.Load,
DatabaseSet,
ServiceSet,
ApplicationSet,
ledger.NewApplication,
)
return nil, nil
}手动依赖注入
go
// 容器实现
type Container struct {
config *config.Config
db *gorm.DB
transactionRepo TransactionRepository
ledgerRepo LedgerRepository
transactionSvc *TransactionService
ledgerSvc *LedgerService
}
func NewContainer(cfg *config.Config) *Container {
return &Container{config: cfg}
}
func (c *Container) GetDB() *gorm.DB {
if c.db == nil {
c.db = database.NewConnection(c.config.Database)
}
return c.db
}
func (c *Container) GetTransactionRepository() TransactionRepository {
if c.transactionRepo == nil {
c.transactionRepo = database.NewTransactionRepository(c.GetDB())
}
return c.transactionRepo
}
func (c *Container) GetTransactionService() *TransactionService {
if c.transactionSvc == nil {
c.transactionSvc = NewTransactionService(
c.GetTransactionRepository(),
c.GetLedgerRepository(),
)
}
return c.transactionSvc
}📚 相关资源
项目实践文档
外部参考
- Clean Architecture - Robert C. Martin
- Design Patterns - Gang of Four
- Domain-Driven Design - Eric Evans
💡 架构建议: 好的架构是演进出来的,不是一开始就设计完美的。根据业务需求的变化,适时重构和优化架构设计。