Skip to content

🏗️ 架构设计模式

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
}

📚 相关资源

项目实践文档

外部参考


💡 架构建议: 好的架构是演进出来的,不是一开始就设计完美的。根据业务需求的变化,适时重构和优化架构设计。

基于 MIT 许可证发布