go中无ddd框架,应以结构体+方法+包边界表达领域概念,聚合根用私有字段和构造函数控制,领域逻辑与技术细节严格分层。

Go 里没有“DDD 框架”,别找 ddd.NewAggregate() 这种东西
Go 语言本身不提供 DDD 原语,也没有官方或主流库封装 AggregateRoot、DomainEvent 等抽象。强行套用其他语言的 DDD 框架(比如 Java 的 Axon 或 .NET 的 EventFlow)到 Go,往往导致过度设计、接口爆炸、测试困难。Go 的惯用法是用结构体 + 方法 + 包边界表达领域概念,而不是靠框架注入生命周期或代理方法。
实操建议:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 用
struct定义聚合根,把业务不变量检查写在构造函数(如NewOrder())和方法(如order.Confirm())里,而不是依赖注解或拦截器 - 聚合内强一致性靠方法封装:所有状态变更必须走聚合根暴露的方法,禁止外部直接改
order.Status - 跨聚合引用只存 ID(
customerID string),不存指针或结构体,避免隐式耦合 - 别为“看起来像 DDD”而加空接口、泛型约束或反射——Go 的类型系统足够表达领域契约,但代价是你要自己写校验逻辑
分层不是靠文件夹命名,而是靠 import 方向和接口定义
很多人建 domain/、application/、infrastructure/ 目录就以为完成了分层。实际只要 domain/ 包 import 了 infrastructure/,或者 application/ 直接 new 了数据库 struct,分层就塌了。
实操建议:
-
domain/包只能 import 标准库(errors、time等)和其他 domain 子包;禁止 importapplication、infrastructure、transport - 应用层(
application/)定义端口接口,例如type OrderRepository interface { Save(Order) error },具体实现放在infrastructure/ - 基础设施层(
infrastructure/)通过application接口反向依赖,不能反向 import;数据库 model 结构体放这里,和 domain 实体分开 - 用
go list -f '{{.ImportPath}} -> {{join .Imports "\n"}}' ./...快速检查 import 循环
Aggregate 不是继承出来的,是靠包级私有字段和构造函数控制的
Go 没有继承,也不鼓励用嵌入模拟“父类”。聚合根的核心是“唯一标识 + 不变量 + 领域事件发布时机”,这些全靠代码纪律保障。
实操建议:
- 聚合根 ID 字段设为
id string(公开),但内部状态字段(如items []OrderItem)设为小写(items),仅通过方法访问 - 构造函数返回指针,并做基础校验:
if id == "" { return nil, errors.New("id required") } - 状态变更方法返回 error,不静默失败:
func (o *Order) Cancel() error { if o.Status != "confirmed" { return errors.New("only confirmed orders can be canceled") } ... } - 领域事件用普通 struct 定义(
type OrderCanceled struct { OrderID string; CanceledAt time.Time }),由聚合方法返回(return []any{&OrderCanceled{...}}),不引入事件总线抽象
别在 domain/ 里处理 JSON、HTTP 或数据库错误
常见错误:在 domain/order.go 里 import encoding/json,或把 sql.ErrNoRows 当成领域错误抛出。这会让领域模型被技术细节污染,且无法复用到 CLI 或 gRPC 场景。
实操建议:
- 序列化逻辑(
MarshalJSON)放在transport/或infrastructure/,domain 层只管业务规则 - 数据库错误(如
sql.ErrNoRows、pgconn.PgError)必须在 infrastructure 层转成领域无关错误(ErrOrderNotFound),再向上抛 - 领域错误用自定义 error var 定义在 domain 包里:
var ErrOrderAlreadyShipped = errors.New("order already shipped"),不带技术栈信息 - HTTP 状态码映射、重试策略、超时设置——全在 transport 层,domain 层连
context.Context都不该感知
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










