文章

为什么写不好 DDD 代码?

见字如面,与大家分享实践中的经验与思考。

自从 DDD 火起来之后,个人主导的项目以及经历的项目绝大部分都采用的是 DDD 模式,今天就来聊一聊,DDD 模式与 MVC 模式的差异以及我对于 DDD 模式的一些看法。

DDD 对 MVC 的演进

1. 依赖反转

根据依赖倒置原则(高层模块不应该依赖于低层模块,两者都应该依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象)。

带来的变化:

  • 基础设施层的接口定义在其他层。基础设施层只实现这些接口。

  • 具体出现依赖反转的场景如下:

    a)在 Domain 层新增了对 Repository 定义的接口。

    b)在 infrastructure 层新增了对 Repository 仓储的实现。

    c)Application 层不再依赖 infrastructure 层。

依赖反转的缺点:

  • Application 层无法直接调用 infrastructure 层,需要经过 Domain 层,再反转调用到 infrastructure 层,导致增加了不同层的数据对象,以及这些对象之间的转换。

  • 思维模式的转变, Api --> Biz Logic --> Db 这种直接调用模式深入人心,一时很难进行转变,而且很多时候确实简单高效,引起很多开发对于 DDD 模式的质疑。

依赖反转的优点:

  • 各层代码边界更加清晰

  • 业务逻辑层不再依赖 infrastructure 层,更容易编写单元测试。

2. 业务逻辑拆分成 Application 层 和 Domain 层

带来的变化:

  • 新增一个 Domain 层。

  • 将业务逻辑层中的领域相关业务提取到新增的 Domain 层。

新增 Domain 层的缺点:

  • 增加了 Domain 层后,domain 层的领域对象需要转换。

  • 对大批量的修改或者性能要求很高的场景支持不够友好。

  • DDD 模式下更加关注单个聚合的生命周期,对于数据的创建和修改,采用的全量的方式,在高并发场景下非常容易出现 ABA 的情况。

  • 对于团队成员的要求较高,需要对于 DDD 有一定的理解,很容易出现采用的是 DDD 模式,但是实现的是 MVC 代码,起到了反作用,增加了修改代码的修改量和复杂度。

新增 Domain 层的优点:

  • 充分理解 DDD 的设计模式后,通过统一的方法论,提高代码设计质量。

  • 保障设计与代码的一致性,提升工程质量。

  • 通过业务与基础设施的解耦,对单体测试的友好性。

为什么写不好 DDD 代码呢?

1 业务理解不足,领域模型脱离实际

在国内项目中,产品需求变动频繁,开发者和业务方之间的沟通往往缺乏深入性和连续性。开发团队为了赶进度,容易忽略领域建模的时间投入,导致领域模型不准确、不完整,甚至完全背离业务需求。

导致领域模型成为形式化的存在,无法清晰表达业务逻辑,开发过程中经常需要额外编码来“弥补”模型的缺陷,增加了代码复杂性和维护成本。

2. 团队技术能力不均,实践 DDD 时急功近利

团队成员的技术能力参差不齐,部分开发者对 DDD 理解不足,仅从“分层架构”角度解读 DDD,而忽视了领域建模和限界上下文的核心思想。同时,项目往往处于紧张的交付节奏中,团队急于应用 DDD,导致模型设计草率,甚至过度复杂化。

导致系统设计过度臃肿或不符合实际业务场景,领域层逻辑分散在应用层或基础设施层中,开发和维护的难度加大。

3. 上下文边界不清,领域模型与技术耦合

国内许多项目因技术驱动而非业务驱动展开,比如优先选择微服务架构、ORM 框架或某种技术解决方案,而在开发过程中再去适配领域模型。限界上下文未能准确划分,领域模型混杂多个上下文的概念,领域逻辑与数据存储和服务接口深度耦合。

导致系统内的领域逻辑难以维护和扩展,代码质量参差不齐,随着业务发展,重构成本越来越高,团队逐渐对 DDD 失去信心。

4. CRUD 场景多,DDD 显得繁琐

在很多复杂业务系统中,尽管整体业务看似复杂,但核心功能可能仍以 CRUD(增删改查)为主,如管理后台、数据录入系统等。这种情况下,直接使用简单的事务脚本或基于传统分层架构的设计就能很好地满足需求,而采用 DDD 的分层与模型设计反而显得繁琐。

强行使用 DDD,导致将简单的逻辑拆分成复杂的聚合、实体、值对象和服务,导致代码臃肿,开发效率下降,且团队对这些额外设计产生不满。

建议

  1. 当项目的业务场景简单,核心逻辑就是 CRUD,直接采用传统的分层架构(Controller-Service-Repository),避免不必要的复杂性。

  2. 当项目的业务复杂度高,但 CRUD 仍占据主导,可采用灵活的分层设计,将 DDD 用于处理关键的复杂业务逻辑,而其他部分仍然保持轻量化设计,做到局部复杂化、全局简单化。

  3. 若采用灵活的分层架构,可以结合 MVC 和 DDD 有优缺点进行整合,团队内部达成一致。

最后

DDD 的核心价值在于解决复杂的领域问题,但对于以 CRUD 操作为主的场景来说,强行使用 DDD 会导致效率低、维护成本高、团队抵触。正确的做法是根据实际需求权衡设计方法,避免理论驱动开发,做到“适场景而用,避免过度设计”。

推荐阅读

DDD 分层架构落地实践

应用分层架构最佳实践:Alibaba COLA 4.0

附录

DDD 工程代码样例:

image-20250113下午31933832


欢迎关注我的公众号“Eric技术圈”,原创技术文章第一时间推送。

License:  CC BY 4.0