go中聚合根是私有字段+公开id的struct,通过构造函数和方法控制状态变更,跨聚合仅存id,领域逻辑限于domain包且不可依赖基础设施。

Go 里没有“聚合根框架”,所谓设计原则,本质是代码纪律的集合:用结构体字段可见性、包级约束和方法封装来守住一致性边界。
聚合根必须是 struct,且 ID 字段公开、状态字段私有
很多人误以为聚合根要继承某个接口或嵌入通用基类,Go 不支持也不需要。真正的聚合根就是一个普通 struct,靠字段大小写控制访问权限。
- ID 字段(如
ID string或ID uuid.UUID)必须大写公开,这是外部唯一引用它的依据 - 所有内部状态字段(如
items []OrderItem、status string)必须小写私有,禁止外部直接读写 - 构造函数(如
NewOrder(id string))是唯一合法创建入口,必须做基础校验(空 ID、格式合法性等) - 状态变更只允许通过方法暴露,例如
o.Confirm()而不是o.status = "confirmed"
聚合内强一致,跨聚合只能存 ID,不能存指针或结构体
这是最容易被绕过的边界。一旦在 Order 里直接嵌入 Customer struct 或持有 *Customer,就等于把两个聚合的生命周期和事务边界焊死了。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 正确做法:只存
CustomerID string,查客户信息由应用层协调,或通过领域事件异步更新 - 错误现象:
order.Customer.Name编译通过 → 立刻违反聚合边界,后续任何重构都会牵一发而动全身 - go list 检查:如果
domain/order包 import 了domain/customer,说明已塌方 - 值对象可嵌入(如
BillingAddress),但必须不可变:新地址 ≠ 修改旧地址,而是替换整个字段
领域逻辑只写在 domain/ 包内,且禁止 import infrastructure/ 和 application/
分层不是靠目录名,而是靠 import 方向。很多项目建了 domain/ 文件夹却仍把数据库 model 放进去,等于没分层。
-
domain/只能 import 标准库(errors、time)、其他domain/xxx子包,以及自定义 error 类型 - 数据库 struct(如
OrderModel)必须放在infrastructure/,它实现的是domain.OrderRepository接口,而非反过来 - 应用层(
application/)定义用例逻辑,调用domain.Order方法 +domain.OrderRepository接口,不碰具体实现 - 常见坑:
domain/order.go里出现"github.com/xxx/infrastructure/db"→ 分层失效,测试无法 mock,领域逻辑被技术细节污染
事务边界 = 聚合根方法调用 + 基础设施层显式事务控制
Go 没有透明事务代理,聚合根方法本身不开启事务;事务必须由基础设施层(如数据库驱动)在调用时显式包裹。
- 聚合根方法(如
o.Cancel())只负责业务规则校验和状态变更,不处理持久化 - 应用层组装逻辑后,交由 infrastructure 层执行:例如用
db.WithTransaction(ctx, ...)包裹repo.Save(order) - go-zero 的
Aggregate()是对 MongoDB 的封装,不适用于 SQL 场景;SQL 用户应依赖sql.Tx手动控制 - 别在聚合根里调
db.Exec或redis.Set—— 这会让领域模型彻底失去可测试性和可移植性
真正难的从来不是写一个 NewOrder(),而是每次想加个字段、改个逻辑、引入个新依赖时,都停下来问一句:这个改动,有没有悄悄跨过聚合边界?
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










