Go 项目结构之争:DDD 还是简洁分层?
Go 项目结构之争:DDD 还是简洁分层? Go 项目一开始通常很简单:几个接口、几张表、几个服务函数,按 handler、service、repository 分开就能跑起来。项目变大之后,团队又开始讨论 DDD、领域模型、聚合、应用层和基础设施层。 于是问题变成了:Go 项目到底应该采用 DDD,还是坚持简洁分层? 我的答案是:不要先选择目录结构,要先识别业务复杂度和变化边界。 简洁分层不是低级方案,DDD 也不是高级方案。它们只是解决不同问题的工具。 可以把项目想成一家餐馆。简洁分层像一家小店:前台接单,后厨做菜,仓库取料,路线短,出了问题也容易找到人。DDD 更像一家菜品、库存、会员和结算规则都很复杂的连锁餐厅:重点不是把岗位拆得越多越好,而是让“什么菜能做、什么时候能退单、库存怎么扣”这些规则有明确的负责人。 小店一开始就照搬连锁餐厅的总部制度,会被流程拖慢;连锁餐厅却不能永远把所有事情都交给一个大厨。Go 项目的结构选择,也是同一个道理。 一、争论表面是目录,本质是边界 很多团队讨论架构时,第一步就是贴目录树: internal/ ├── handler/ ├── service/ ├── repository/ └── model/ 或者: internal/ ├── domain/ ├── application/ ├── interfaces/ └── infrastructure/ 但目录本身不会自动带来好的架构。真正重要的是几个问题: 哪些代码属于同一个业务能力? 哪些规则必须始终成立? 哪些变化应该被隔离? 数据库、消息队列等技术细节,是否侵入了业务决策? 一个需求发生变化时,需要修改多少个包? 如果这些问题没有答案,换一套目录只是换了一种命名方式。 架构的价值不在于让目录看起来专业,而在于让变化停留在应该停留的地方。 先用一张图看两种结构的关注点:简洁分层按“技术职责”排队,DDD 更关心“业务规则”应该被谁拥有。 flowchart LR subgraph simple["简洁分层:按技术分组"] S1["请求"] --> S2["handler"] --> S3["service"] --> S4["repository"] --> S5[("数据库")] S3 -. "规则容易集中" .-> S6["变胖的 service"] end subgraph ddd["DDD:按业务边界组织"] D1["请求"] --> D2["应用用例"] --> D3["领域规则"] D2 --> D4["仓储接口"] D4 -. "具体实现" .-> D5["基础设施"] D5 --> D6[("数据库")] end 图中没有“谁更高级”的答案。左边像小餐馆的传菜路线,胜在短;右边像连锁餐厅的岗位边界,胜在业务规则变复杂时,能把变化关在领域边界里。 ...