mengstack-website/guide/module-pattern.md
2026-10-02 23:55:36 +08:00

3.5 KiB
Raw Permalink Blame History

模块四层模式

每个业务模块内部采用四层结构:Domain → Application → Infrastructure → Interfaces。

四层结构

┌─────────────────────────────────────┐
│           Interfaces                │  ← HTTP Handler, 路由注册
├─────────────────────────────────────┤
│          Application                │  ← 业务逻辑编排
├─────────────────────────────────────┤
│        Infrastructure               │  ← GORM Model, Redis 实现
├─────────────────────────────────────┤
│            Domain                   │  ← 实体, 值对象, 接口定义
└─────────────────────────────────────┘

依赖方向

从上到下依赖,Domain 在最底层,不依赖任何外部包。

层 职责 可以依赖 禁止依赖
Interfaces HTTP 入口 Application, Domain Infrastructure
Application 业务编排 Domain Infrastructure, Interfaces
Infrastructure 数据持久化 Domain Application, Interfaces
Domain 业务核心 标准库 所有外部包

Domain 层

定义业务的核心概念和契约:

// domain/entity.go
type User struct {
    ID       uint
    Email    string
    Username string
    Password string
    TenantID string
}

// domain/repository.go — 定义接口,不定义实现
type UserRepository interface {
    Create(ctx context.Context, user *User) error
    FindByEmail(ctx context.Context, email string) (*User, error)
}

Domain 层零外部依赖,只使用 Go 标准库。这使得业务逻辑可以独立于框架和数据库进行单元测试。

Application 层

编排业务流程,调用 Domain 层定义的接口:

// application/service.go
type Service struct {
    repo    domain.UserRepository
    hasher  domain.PasswordHasher
    jwt     domain.TokenManager
}

func (s *Service) Register(ctx context.Context, req domain.RegisterRequest) (*domain.TokenPair, error) {
    // 1. 验证邮箱唯一性
    // 2. 密码哈希
    // 3. 创建用户
    // 4. 生成 JWT
    // 5. 返回 Token
}

Application 层不知道数据是怎么存的(GORM?Redis?),它只依赖 Domain 层定义的接口。

Infrastructure 层

提供 Domain 层接口的具体实现:

// infrastructure/repository.go
type GormUserRepository struct {
    db *gorm.DB
}

func (r *GormUserRepository) Create(ctx context.Context, user *domain.User) error {
    // GORM 实现
}

Infrastructure 层实现 Domain 层定义的接口,但 Domain 层不知道 Infrastructure 的存在。

Interfaces 层

HTTP 入口,负责请求解析和响应:

// interfaces/handler.go
func (h *Handler) Register(c *gin.Context) {
    var req domain.RegisterRequest
    c.ShouldBindJSON(&req)
    tokens, err := h.svc.Register(c.Request.Context(), req)
    response.Success(c, tokens)
}

好处

  1. 可测试:Domain 层可以脱离数据库进行单元测试
  2. 可替换:换数据库只需写新的 Infrastructure 实现
  3. 边界清晰:每层职责明确,新人容易理解
  4. 防止腐败:Gin 的 c *gin.Context 不会渗透到业务逻辑中