golang-ddd

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

DDD Tactical Design Patterns in Go

Go语言中的DDD战术设计模式

Purpose

目的

To guide implementation of Domain-Driven Design tactical patterns in idiomatic Go. This skill covers Entities, Value Objects, Aggregates, Repositories, Domain Services, Domain Events, Factories, and Specifications using Go-native idioms (composition over inheritance, interfaces, unexported fields, functional options).
指导你使用Go语言的惯用风格实现领域驱动设计(DDD)的战术模式。本技能涵盖实体、值对象、聚合、仓库、领域服务、领域事件、工厂和规约模式,采用Go原生的惯用写法(组合优于继承、接口、未导出字段、函数式选项)。

When to Use This Skill

适用场景

  • Implementing domain models with rich business logic in Go
  • Designing aggregate boundaries and consistency rules
  • Creating repository interfaces and infrastructure implementations
  • Building event-driven domain models
  • Structuring a Go project following DDD layered architecture
  • Reviewing domain code for DDD pattern adherence
  • 在Go中实现包含丰富业务逻辑的领域模型
  • 设计聚合边界与一致性规则
  • 创建仓库接口及基础设施实现
  • 构建事件驱动的领域模型
  • 遵循DDD分层架构搭建Go项目结构
  • 评审领域代码是否符合DDD模式规范

Core Principles

核心原则

  1. Go idioms first - No Java-style OOP. Use composition, interfaces, and package boundaries
  2. Unexported fields - All entity/aggregate fields lowercase; expose through getters and behavior methods
  3. Pointer receivers for entities - Mutable domain objects use
    *T
    receivers
  4. Value receivers for value objects - Immutable types use
    T
    receivers
  5. Factory functions - Use
    NewX()
    constructors to enforce invariants at creation
  6. Interface in domain, implementation in infrastructure - Repository interfaces live with aggregates
  7. One package per aggregate - Each aggregate root gets its own package under
    internal/domain/
  8. Context propagation - Pass
    context.Context
    as first parameter in repository and service methods
  1. 优先遵循Go语言惯用风格 - 不使用Java风格的面向对象,采用组合、接口和包边界实现
  2. 未导出字段 - 所有实体/聚合的字段使用小写命名;通过访问器方法和行为方法暴露功能
  3. 实体使用指针接收器 - 可变领域对象使用
    *T
    类型接收器
  4. 值对象使用值接收器 - 不可变类型使用
    T
    类型接收器
  5. 工厂函数 - 使用
    NewX()
    构造函数在创建时强制校验不变量
  6. 接口定义在领域层,实现放在基础设施层 - 仓库接口与聚合放在同一包中
  7. 每个聚合对应一个包 - 每个聚合根在
    internal/domain/
    下拥有独立的包
  8. 上下文传递 - 在仓库和服务方法中,将
    context.Context
    作为第一个参数

Project Structure

项目结构

internal/
├── domain/                     # Domain layer (no external deps)
│   ├── customer/               # One package per aggregate
│   │   ├── customer.go         # Aggregate root entity
│   │   ├── repository.go       # Repository interface
│   │   ├── email.go            # Value objects
│   │   ├── events.go           # Domain events
│   │   └── errors.go           # Domain errors
│   ├── order/
│   │   ├── order.go
│   │   ├── repository.go
│   │   ├── item.go             # Child entity
│   │   └── money.go            # Value object
│   └── shared/                 # Shared kernel
│       ├── events.go           # Event interface
│       └── specification.go    # Generic specification
├── application/                # Application services (orchestration)
│   ├── command/
│   │   └── place_order.go
│   └── query/
│       └── get_customer.go
└── infrastructure/             # Technical implementations
    ├── postgres/
    │   ├── customer_repo.go
    │   └── order_repo.go
    └── eventbus/
        └── in_memory.go
Dependency rule: Domain has zero imports from application or infrastructure. Dependencies point inward.
internal/
├── domain/                     # 领域层(无外部依赖)
│   ├── customer/               # 每个聚合对应一个包
│   │   ├── customer.go         # 聚合根实体
│   │   ├── repository.go       # 仓库接口
│   │   ├── email.go            # 值对象
│   │   ├── events.go           # 领域事件
│   │   └── errors.go           # 领域错误
│   ├── order/
│   │   ├── order.go
│   │   ├── repository.go
│   │   ├── item.go             # 子实体
│   │   └── money.go            # 值对象
│   └── shared/                 # 共享内核
│       ├── events.go           # 事件接口
│       └── specification.go    # 通用规约
├── application/                # 应用服务层(编排逻辑)
│   ├── command/
│   │   └── place_order.go
│   └── query/
│       └── get_customer.go
└── infrastructure/             # 技术实现层
    ├── postgres/
    │   ├── customer_repo.go
    │   └── order_repo.go
    └── eventbus/
        └── in_memory.go
依赖规则: 领域层不导入应用层或基础设施层的任何内容,依赖关系向内指向领域层。

Pattern Quick Reference

模式速查表

PatternGo IdiomReceiverIdentity
EntityStruct + pointer receiver
*T
By ID
Value ObjectType alias or struct + value receiver
T
By value
Aggregate RootEntity + unexported children
*T
By ID
RepositoryInterface in domain packageN/AN/A
Domain ServiceStateless struct with deps
*T
or func
N/A
Domain EventImmutable struct
T
(value)
By name+time
Factory
NewX()
function
N/AN/A
SpecificationGeneric interface
IsSatisfiedBy(T) bool
T
or
*T
N/A
模式Go惯用写法接收器类型标识方式
实体结构体 + 指针接收器
*T
通过ID
值对象类型别名或结构体 + 值接收器
T
通过值内容
聚合根实体 + 未导出子对象
*T
通过ID
仓库接口定义在领域包中N/AN/A
领域服务无状态结构体(包含依赖)
*T
或函数
N/A
领域事件不可变结构体
T
(值类型)
通过名称+时间
工厂
NewX()
函数
N/AN/A
规约泛型接口
IsSatisfiedBy(T) bool
T
*T
N/A

Implementation Workflow

实现流程

When implementing a new aggregate or domain concept:
  1. Define value objects - Create self-validating types for domain primitives (
    Email
    ,
    Money
    ,
    Address
    )
  2. Define entities - Create types with identity, unexported fields, and behavior methods
  3. Define aggregate root - Designate one entity as root; enforce all invariants through its methods
  4. Define repository interface - Place interface in same package as aggregate root
  5. Define domain events - Create immutable event structs for significant state changes
  6. Implement infrastructure - Create repository implementations in
    infrastructure/
    package
  7. Wire application layer - Create command/query handlers that orchestrate domain operations
当实现新的聚合或领域概念时:
  1. 定义值对象 - 为领域原语创建自验证类型(如
    Email
    Money
    Address
  2. 定义实体 - 创建包含标识、未导出字段和行为方法的类型
  3. 定义聚合根 - 指定一个实体作为根;通过其方法强制校验所有不变量
  4. 定义仓库接口 - 将接口放在与聚合根相同的包中
  5. 定义领域事件 - 为重要的状态变更创建不可变的事件结构体
  6. 实现基础设施层 - 在
    infrastructure/
    包中创建仓库的具体实现
  7. 组装应用层 - 创建命令/查询处理器,编排领域操作

Pattern Details

模式细节

For detailed implementation guides with full code examples, see:
  • references/entities-and-value-objects.md
    - Entities, Value Objects, and Factories
  • references/aggregates-and-repositories.md
    - Aggregates, Repositories, and Domain Services
  • references/events-and-specifications.md
    - Domain Events and Specifications
  • references/anti-patterns.md
    - Common mistakes and how to avoid them
如需包含完整代码示例的详细实现指南,请查看:
  • references/entities-and-value-objects.md
    - 实体、值对象与工厂
  • references/aggregates-and-repositories.md
    - 聚合、仓库与领域服务
  • references/events-and-specifications.md
    - 领域事件与规约
  • references/anti-patterns.md
    - 常见错误及规避方法

Entities

实体

Entities have unique identity and mutable state. Use unexported fields, pointer receivers, and factory functions.
go
type Order struct {
    id        uuid.UUID
    status    Status
    items     []Item
    createdAt time.Time
}

func NewOrder(customerID uuid.UUID) (*Order, error) {
    return &Order{
        id:        uuid.New(),
        status:    StatusDraft,
        items:     make([]Item, 0),
        createdAt: time.Now(),
    }, nil
}

func (o *Order) AddItem(product ProductID, qty int, price Money) error {
    if o.status != StatusDraft {
        return ErrOrderNotDraft
    }
    o.items = append(o.items, NewItem(product, qty, price))
    return nil
}
实体拥有唯一标识和可变状态。使用未导出字段、指针接收器和工厂函数。
go
type Order struct {
    id        uuid.UUID
    status    Status
    items     []Item
    createdAt time.Time
}

func NewOrder(customerID uuid.UUID) (*Order, error) {
    return &Order{
        id:        uuid.New(),
        status:    StatusDraft,
        items:     make([]Item, 0),
        createdAt: time.Now(),
    }, nil
}

func (o *Order) AddItem(product ProductID, qty int, price Money) error {
    if o.status != StatusDraft {
        return ErrOrderNotDraft
    }
    o.items = append(o.items, NewItem(product, qty, price))
    return nil
}

Value Objects

值对象

Immutable types validated at creation. Use value receivers. Return new instances for operations.
go
type Money struct {
    amount   int64
    currency string
}

func NewMoney(amount int64, currency string) (Money, error) {
    if currency == "" {
        return Money{}, ErrInvalidCurrency
    }
    return Money{amount: amount, currency: currency}, nil
}

func (m Money) Add(other Money) (Money, error) {
    if m.currency != other.currency {
        return Money{}, ErrCurrencyMismatch
    }
    return Money{amount: m.amount + other.amount, currency: m.currency}, nil
}
创建时完成验证的不可变类型。使用值接收器,操作返回新实例。
go
type Money struct {
    amount   int64
    currency string
}

func NewMoney(amount int64, currency string) (Money, error) {
    if currency == "" {
        return Money{}, ErrInvalidCurrency
    }
    return Money{amount: amount, currency: currency}, nil
}

func (m Money) Add(other Money) (Money, error) {
    if m.currency != other.currency {
        return Money{}, ErrCurrencyMismatch
    }
    return Money{amount: m.amount + other.amount, currency: m.currency}, nil
}

Aggregates

聚合

Aggregate roots enforce invariants across child entities. All mutations go through the root.
go
func (o *Order) Place() error {
    if len(o.items) == 0 {
        return ErrEmptyOrder
    }
    if o.status != StatusDraft {
        return ErrOrderNotDraft
    }
    o.status = StatusPlaced
    o.events = append(o.events, NewOrderPlacedEvent(o.id, o.Total()))
    return nil
}
聚合根强制校验子实体的所有不变量,所有修改操作必须通过根实体完成。
go
func (o *Order) Place() error {
    if len(o.items) == 0 {
        return ErrEmptyOrder
    }
    if o.status != StatusDraft {
        return ErrOrderNotDraft
    }
    o.status = StatusPlaced
    o.events = append(o.events, NewOrderPlacedEvent(o.id, o.Total()))
    return nil
}

Repositories

仓库

Interface in domain, implementation in infrastructure. One repository per aggregate root.
go
// domain/order/repository.go
type Repository interface {
    Find(ctx context.Context, id uuid.UUID) (*Order, error)
    Save(ctx context.Context, order *Order) error
    Update(ctx context.Context, id uuid.UUID, fn func(*Order) error) error
}
接口定义在领域层,实现放在基础设施层。每个聚合根对应一个仓库。
go
// domain/order/repository.go
type Repository interface {
    Find(ctx context.Context, id uuid.UUID) (*Order, error)
    Save(ctx context.Context, order *Order) error
    Update(ctx context.Context, id uuid.UUID, fn func(*Order) error) error
}

Domain Events

领域事件

Immutable structs collected by aggregates, published by application layer.
go
type OrderPlaced struct {
    orderID    uuid.UUID
    total      Money
    occurredAt time.Time
}

func (o *Order) PullEvents() []Event {
    events := o.events
    o.events = nil
    return events
}
由聚合收集的不可变结构体,由应用层发布。
go
type OrderPlaced struct {
    orderID    uuid.UUID
    total      Money
    occurredAt time.Time
}

func (o *Order) PullEvents() []Event {
    events := o.events
    o.events = nil
    return events
}

Domain Services

领域服务

Stateless operations spanning multiple aggregates. Domain logic only, no orchestration.
go
type TransferService struct {
    accountRepo account.Repository
}

func (s *TransferService) Transfer(ctx context.Context, from, to uuid.UUID, amount Money) error {
    // Load aggregates, validate domain rules, coordinate changes
}
跨多个聚合的无状态操作,仅包含领域逻辑,不负责编排。
go
type TransferService struct {
    accountRepo account.Repository
}

func (s *TransferService) Transfer(ctx context.Context, from, to uuid.UUID, amount Money) error {
    // 加载聚合、校验领域规则、协调变更
}

Specifications

规约

Composable business rules using Go generics.
go
type Specification[T any] interface {
    IsSatisfiedBy(T) bool
}

func And[T any](specs ...Specification[T]) Specification[T] { /* ... */ }
func Or[T any](specs ...Specification[T]) Specification[T]  { /* ... */ }
func Not[T any](spec Specification[T]) Specification[T]     { /* ... */ }
使用Go泛型实现的可组合业务规则。
go
type Specification[T any] interface {
    IsSatisfiedBy(T) bool
}

func And[T any](specs ...Specification[T]) Specification[T] { /* ... */ }
func Or[T any](specs ...Specification[T]) Specification[T]  { /* ... */ }
func Not[T any](spec Specification[T]) Specification[T]     { /* ... */ }

Key Rules

核心规则

  • Never expose aggregate internals - No public fields, no getters that return mutable child collections
  • No setters - Replace
    SetStatus()
    with domain methods like
    Place()
    ,
    Cancel()
    ,
    Ship()
  • Reference other aggregates by ID - Never hold direct pointers to other aggregate roots
  • Reconstitution factories - Create separate
    Reconstruct()
    functions for loading from DB (bypass validation)
  • Domain errors - Define sentinel errors (
    var ErrNotFound = errors.New(...)
    ) per aggregate package
  • Accept interfaces, return structs - Repository parameters use interfaces; factories return concrete types
  • 绝不暴露聚合内部细节 - 无公共字段,无返回可变子集合的访问器
  • 不使用Setter方法 - 用
    Place()
    Cancel()
    Ship()
    等领域方法替代
    SetStatus()
  • 通过ID引用其他聚合 - 绝不直接持有其他聚合根的指针
  • 重构工厂 - 创建独立的
    Reconstruct()
    函数用于从数据库加载(跳过验证)
  • 领域错误 - 每个聚合包中定义哨兵错误(如
    var ErrNotFound = errors.New(...)
  • 依赖接口,返回结构体 - 仓库参数使用接口;工厂返回具体类型

References

参考资料

Detailed guides with full code examples are in the
references/
directory.
包含完整代码示例的详细指南位于
references/
目录下。