go-ddd揭示的10条DDD最佳实践与常见误区(含‘DDD是否过度设计‘答案)
发布时间:2026/10/26 22:55:04 锦皓数字建站
`)
【免费下载链接】go-dddOpinionated Domain Driven Design Template for Go项目地址https://gitcode.com/gh_mirrors/god/go-ddd点击查看免费下载还在纠结 DDDDomain-Driven Design是不是过度设计本文基于生产级 Go 模板go-ddd给出明确答案它是有主见的 DDD 脚手架内置洋葱架构、CQRS、幂等写入与事务性 Outbox可直接 clone 落地。下面用 10 条经过实战检验的 DDD 最佳实践、5 个最常见的踩坑误区帮你快速建立正确的领域建模认知。 一分钟看懂go-ddd 的洋葱架构依赖箭头只指向内侧位于中心的domain不依赖任何外层数据库、HTTP 框架对它都是透明的插件。这也是整套 DDD 最佳实践的骨架层职责目录domain实体、值对象、事件、仓储接口纯业务规则internal/domain/application命令、查询、应用服务编排internal/application/infrastructuresqlc 仓储实现、Outbox 中继、配置internal/infrastructure/interfaceREST 控制器、DTO、错误映射internal/interface/api/rest/唯一知道所有层的接线点是 cmd/marketplace/main.go。完整的分层规则可参考 docs/reference/architecture.md。✅ 10条 DDD 最佳实践go-ddd 版1. 先统一通用语言命名即设计业务说 seller那么结构体是Seller、表是sellers、接口是/sellers而不是 API 叫Vendor、数据库叫Merchant。更妙的是让类型签名替规则说话NewProduct的第三个参数是ValidatedSeller而非Seller——没通过校验的卖家根本装不进这个函数。详见 docs/tutorial/01-the-domain.md。2. 用构造函数建实体用ValidatedX类型把校验变成编译期事实贫血模型的问题字段全公开、无构造函数规则散落在各处if里。go-ddd 的模式是所有实体经NewX构造函数出生不变量在出生时确立仓储写入接口只接受ValidatedProduct这类已校验类型忘校验 编译错误。这套模式的完整推演见 docs/tutorial/02-entities.md 与 internal/domain/entities/product.go。3. 金额用 Money 值对象永远别用 float640.1 0.2 0.3为 false 是老生常谈真正的伤害是累计漂移和一个没有单位的裸数字。go-ddd 的 Money 值对象整数最小货币单位minor units 内联币种 私有字段 唯一构造函数非法状态根本构造不出来连 JSON 反序列化都会绕回构造函数重新校验。这是任何碰过价格的代码库都值得抄的一个模式。4. 聚合之间只按 Id 引用聚合保持小Product存的是SellerId uuid.UUID不是内嵌的Seller对象。内嵌会引发撕裂的所有权、隐式的事务蔓延和加载膨胀。画边界的标准只有一条哪些不变量必须在每次提交时立即一致其余跨聚合的诉求交给事件或应用层协调。边界划分方法论在 docs/tutorial/04-aggregates.md。5. 仓储接口放领域层实现放基础设施层ProductRepository 接口里没有*sql.DB、没有 ORM领域由此拥有持久化需求基础设施反过来依赖领域——依赖倒置真正在做实换数据库引擎不用动一行业务代码服务测试用内存 fake 毫秒级跑完。实现侧推荐 sqlc 这类显式、可审查的方案而非反射式 ORM见 docs/tutorial/05-repositories.md。6. CQRS读写分离但一个数据库就够写请求走完整领域模型校验、幂等、事件读请求走一条没有业务规则的薄路径返回ProductResult这类哑输出结构。收益读端点不再付写路径的开销未来给某个热查询换一条手调 SQL 甚至独立读库写路径零改动。刻意不上的重型装备总线、独立读库、事件溯源在 docs/tutorial/06-cqrs.md 有诚实说明。7. 软删除deleted_at 让数据看不见而非消失所有写操作配deleted_at列所有读查询过滤deleted_at IS NULL。误删可恢复、历史可追溯且已删除的实体不存在这一规则集中在每个仓储的 SQL 里统一执行。8. 领域事件 事务性 Outbox告别双写难题先提交数据库、再发 Kafka必然后患崩溃后要么丢事件、要么幽灵事件。go-ddd 的解法事件由聚合自身记录与聚合行同一个事务写入outbox_events表再由一个笨中继轮询发布至少一次投递消费端按 UUIDv7 事件 Id 去重。忘记发布不复存在因为没有发布步骤可忘。表结构与部分索引见 migrations/000003_outbox.up.sql 和 docs/tutorial/07-domain-events-outbox.md。9. 幂等键原子抢占而非先查后写客户端超时重试是常态而非异常。先Find再Create的幂等实现存在并发竞态——同一把 key 的两个请求会双双执行。正确姿势是INSERT ... ON CONFLICT DO NOTHING原子预留靠影响行数决定谁有资格执行见 sql/queries/idempotency.sql再配上 TTL 接管、同 key 不同 payload 返回 422、失败即释放、context.WithoutCancel清理等细节完整复盘在 docs/tutorial/08-idempotency.md。10. 默认值放在领域层别放数据库ID、创建时间这类业务默认值由领域构造函数统一生成。原因避免两个事实来源、领域层更易测试、换数据库时不用重写一堆 DB 默认值。⚠️ 5个最常见的 DDD 误区误区后果go-ddd 的解法金额用float64累计误差、币种歧义Money值对象整数最小单位 币种白名单幂等先查后写并发重试下仍产生重复数据唯一约束 ON CONFLICT原子抢占双写事件库MQ崩溃后数据漂移或幽灵事件事务性 Outbox 中继领域层 import 了框架/驱动模型被基础设施绑架换库要重写规则洋葱架构依赖只向内侧指大聚合Seller 内嵌全部 Product写并发串行化、加载膨胀聚合按 Id 互引边界只按强一致不变量划分 DDD 是否过度设计官方 FAQ 的直白回答有时是的。如果你的服务只是薄薄一层 CRUD——表单进、行出规则只有必填——那么实体、值对象、仓储间接层就是没有回报的仪式用朴素的 handler database 就好。分水岭是规则的到来钱、库存、状态机、权限任何业务专家会回答看情况的东西。作者的判断法则如果你把同一个if写在了两个地方或者不能在 10 秒内回答X 的规则在哪里——你就已经越过了那条线。而且 DDD不是全有或全无哪怕只引入一个Money值对象在任何碰价格的代码库里都划算。更多权衡讨论见 docs/reference/faq.md。 30秒跑起来需要 Go 1.26clone 仓库后一条命令拉起 Postgres APIgit clone https://gitcode.com/gh_mirrors/god/go-ddd cd go-ddd make docker-up # API 位于 http://localhost:8080然后curl创建卖家、商品各一次用同一个idempotency_key重放请求即可亲眼看到幂等缓存返回、不产生第二行数据。更多目标见 MakefileAPI 契约见 api/openapi.yaml。 延伸阅读九章式 DDD 教程从领域语言到测试docs/tutorial/架构一页纸速查docs/reference/architecture.md决策背后的取舍与 FAQdocs/reference/faq.md整体 DDD CQRS 导读DDD_CQRS_PRINCIPLES.md赞分享【免费下载链接】go-dddOpinionated Domain Driven Design Template for Go项目地址https://gitcode.com/gh_mirrors/god/go-ddd点击查看免费下载相关推荐DDD实战宝典10个必须掌握的领域驱动设计最佳实践DDD实战宝典10个必须掌握的领域驱动设计最佳实践 领域驱动设计DDD是构建复杂软件系统的强大方法论而GitHub加速计划中的 dddsample co后端终极指南Python DDD 项目常见问题解决方案与最佳实践终极指南Python DDD 项目常见问题解决方案与最佳实践 Python DDD领域驱动设计项目是实现复杂业务逻辑的理想选择但在开发过程中可能会遇到各示例工程后端DDD掌握DDD聚合的边界go-ddd中Seller与Product聚合设计完全指南掌握DDD聚合的边界go ddd中Seller与Product聚合设计完全指南 go ddd 是一个为 Go 语言打造的有主见的DDD领域驱动设计模板创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。