MVC到DDD:从架构分层到业务建模的思考

在后端开发学习和实践过程中,我们总会被各种架构模式和设计思想围绕——MVC、Service+DAO分层、DDD四层架构(DDD并非与四层架构绑定 DDD是思想,四层架构是代码落地的一个架构)……这些概念之间到底是替代关系还是互补关系?为什么简单业务用Service+DAO就够,复杂业务却要引入DDD?结合近期的学习思考,我想梳理清楚这些架构之间的核心关联,帮自己(也希望帮到同样困惑的同学)建立清晰的认知。

一、起点:MVC的核心价值——解决“代码放哪”的基础问题

最早接触后端开发时,MVC是绕不开的入门架构。它的核心思想非常简单:将软件分为三个核心层级,实现“展示与逻辑”的初步解耦。

  • Controller(控制器):接收用户请求、协调后续处理、返回响应,相当于系统的“入口/出口”,不承载核心业务逻辑;

  • Model(模型):封装数据和核心业务逻辑,是系统的“大脑”,负责数据的增删改查和业务规则校验;

  • View(视图):展示数据,与用户直接交互(前后端分离场景下,前端页面即View层,后端不再关注)。

MVC的价值在于给出了“代码该放哪”的明确答案,解决了早期开发中“业务逻辑与页面展示混在一起”的混乱问题。但它的局限性也很明显:Model层是一个“大黑盒”,没有明确的内部划分标准——当业务简单时,一个Service+Mapper就能撑起Model层;但当业务复杂(比如电商订单履约、金融风控)时,直接把所有逻辑堆在Service里,很快就会导致代码臃肿、逻辑混乱、难以维护。

二、演进:Service+DAO分层——MVC Model层的简易拆分

为了弥补MVC Model层的模糊性,在Spring生态实践中,我们自然演化出了“Controller+Service+DAO(Mapper)”的三层落地方案。这本质上是对MVC的Model层做了第一次“粗粒度拆分”,让职责更清晰:

  • Controller层:完全对应MVC的Controller层,负责请求接收、参数简单校验、调用Service层;

  • Service层:对应MVC Model层的“业务逻辑核心”,实现订单支付、库存扣减等核心业务规则;

  • DAO(Mapper)层:对应MVC Model层的“数据访问部分”,仅负责与数据库交互,不包含任何业务逻辑。

这种拆分方案简单直观、开发效率高,非常适合简单CRUD场景(比如小型管理后台、个人工具)。但它的问题在复杂业务场景下会暴露无遗:Service层很容易变成“万能层”——既要处理跨实体的业务逻辑(比如支付需要协调订单、库存、支付三个模块),又要兼顾事务控制、数据组装等辅助逻辑,最终导致Service层代码臃肿、逻辑缠绕,后续迭代和维护成本极高。

三、进阶:DDD四层架构——MVC Model层的精细化升级

当业务复杂度上升到一定程度,DDD(领域驱动设计)就成了更优的选择。很多人会误以为DDD是“替代MVC”的架构,其实不然——DDD本质上是一套“业务建模方法论”,它在MVC的大框架下,对Model层做了更精细的拆分,让复杂业务逻辑更清晰、更贴合业务本身。

DDD提出的四层架构(用户层/接口层、应用层、领域层、基础设施层),与MVC、Service+DAO分层的对应关系非常清晰,核心结论先抛出来:DDD的用户层对应MVC的Controller层,后三层共同构成MVC的Model层;DDD与MVC完全兼容,只是对Model层的拆分更彻底

1. DDD四层架构的核心职责与对应关系

DDD四层架构核心职责对应MVC分层对应Service+DAO分层Spring实践落地
用户层(接口层)接收外部请求(HTTP/RPC/消息)、返回响应、参数非业务校验、权限拦截Controller层Controller层@RestController、ControllerAdvice、RPC接口
应用层协调领域层执行、处理事务、组装返回数据,无核心业务逻辑Model层Service层的“协调部分”XXXApplicationService(应用服务)
领域层封装核心业务规则、聚合根/实体/值对象、跨实体业务逻辑,系统核心Model层(核心)Service层的“核心业务部分”聚合根(Entity)、XXXDomainService(领域服务)
基础设施层封装技术细节(数据库、缓存、第三方接口),为上层提供技术支撑Model层DAO(Mapper)层Repository、Mapper、RedisTemplate、Feign客户端

2. 关键演进:从Service层到“应用服务+领域服务”

DDD对Service+DAO分层的核心优化,就是把传统“厚Service”拆成了“薄应用服务”和“厚领域服务”,让业务逻辑的归属更明确:

  • 领域服务:只负责核心业务逻辑,比如“订单支付”需要校验订单状态、扣减库存、更新支付状态等跨实体逻辑,都放在领域服务;

  • 应用服务:不碰核心业务,只做“协调工作”——比如开启事务、调用领域服务、把领域层的结果组装成前端需要的格式。

同时,DDD还强调“业务驱动建模”:先梳理业务领域的规则(比如“未支付订单24小时自动取消”),再把这些规则封装到实体类中(比如Order实体的canPay()、markAsPaid()方法),而不是像传统Service+DAO那样,把所有业务逻辑都写在Service的if-else里。这种方式让代码更贴合业务,后续迭代时也更容易理解和修改。

四、核心认知:所有架构的本质都是“解耦”,只是粒度不同

梳理完从MVC到Service+DAO,再到DDD的演进脉络,会发现一个核心规律:所有架构分层的最终目标都是“解耦”,只是针对不同业务复杂度,选择了不同的拆分粒度

  • MVC:解决“展示与逻辑”的解耦,给出最基础的分层框架;

  • Service+DAO:解决MVC Model层的内部解耦,让数据访问和业务逻辑分离,适配简单业务;

  • DDD:解决复杂业务下Model层的深度解耦,让核心业务逻辑、协调逻辑、技术细节彻底分离,适配复杂业务。

很多人会误以为“DDD比MVC高级”“用了DDD就不能用MVC”,这其实是误区。DDD和MVC不是对立关系,而是互补关系——MVC提供了整体的架构骨架,DDD则是对这个骨架中“Model层”的精细化设计指南。

五、实践建议:根据业务复杂度选择合适的架构

架构没有“最优解”,只有“最合适”。结合自己的实践经验,给出几点选择建议:

  1. 简单CRUD场景(小型管理后台、个人工具):直接用“Controller+Service+DAO”,开发效率高、维护成本低,没必要过度设计;

  2. 中等复杂度场景(常规业务系统):可以借鉴DDD的思想,在Service层内部做简单拆分(比如把核心业务逻辑抽成单独的方法或类),避免Service层臃肿;

  3. 高复杂度场景(电商、金融、供应链):推荐使用DDD四层架构,通过领域建模让复杂业务逻辑更清晰,为长期迭代打下基础;

  4. 渐进式改造:不用一开始就全盘重构为DDD,可以从“拆分Service层”开始,先把核心业务逻辑抽成领域服务,再逐步完善应用层和基础设施层,降低改造风险。

六、DDD跟四层架构什么关系?

DDD(领域驱动设计)本身不强制规定 “四层架构”。DDD 的核心在于战略设计(限界上下文、领域模型)和战术设计(实体、值对象、聚合根、领域服务、仓储等),而 “四层架构” 确实是为了让这些战术设计模式在代码层面更好落地而总结出的一套经典工程实践。

在实际落地 DDD 时,代码架构的选择确实非常重要。虽然 DDD 是一种思想,不强求特定目录结构,但业界已经沉淀出两套最主流的架构模式:

  1. 经典的四层架构(Layered Architecture) —— 最容易理解,适合起步。
  2. 整洁/六边形架构(Clean / Hexagonal Architecture) —— 依赖倒置更彻底,现代微服务主流。

此外,国内 Java 圈(特别是阿里系)还常用 COLA 架构

下面详细拆解这几种架构的代码目录结构和核心要点。


方案一:经典的四层架构 (Standard 4-Layer)

这是最符合上文所提出观点的理解的结构,结构清晰,适合从三层架构转型过来的团队。

核心原则

依赖倒置(DIP):虽然物理上是四层,但在逻辑上,领域层(Domain)必须是独立的

  • 错误的做法:Domain 层 import Infrastructure 层的代码。
  • 正确的做法:Domain 层定义接口(Repository Interface),Infrastructure 层去实现它。

目录结构示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
com.company.project
├── interfaces (用户接口层 / Web)
│ ├── controller // 处理 HTTP 请求
│ └── dto // 数据传输对象 (Request/Response)
├── application (应用层)
│ ├── service // 应用服务 (流程编排)
│ └── assembler // DTO 与 Entity 的转换器
├── domain (领域层 - 核心)
│ ├── model // 聚合根、实体、值对象
│ │ ├── aggregate // 聚合
│ │ └── entity // 实体
│ ├── service // 领域服务 (跨实体逻辑)
│ └── repository // 仓储接口 (注意:这里只放接口!)
└── infrastructure (基础设施层)
├── repository // 仓储实现 (Impl,这里依赖 JPA/MyBatis)
├── entity // PO (Persistent Object,对应数据库表)
└── util // 通用工具

方案二:整洁架构 / 六边形架构 (Clean / Hexagonal)

这是目前 DDD 社区最推荐的架构,特别是在微服务场景下。它不再强调“上下层”,而是强调“内外”。

  • 内核(Inner):Domain + Application。这是雷打不动的业务核心。
  • 外壳(Outer):Web、Database、Redis、MQ。这些都是“插件”。

核心原则

依赖只能由外向内。外壳(Web、DB)依赖内核(Domain),内核谁也不依赖。

目录结构示例

这种架构通常会把文件夹分为 adapter(适配器/外壳)和 core(内核)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
com.company.project
├── adapter (适配器层 - 对应“六边形”的端口)
│ ├── inbound (输入端 / 驱动端)
│ │ └── web // RestController
│ └── outbound (输出端 / 被驱动端)
│ ├── persistence // 数据库实现 (Repository Impl)
│ └── thirdparty // 第三方调用 (如调用支付接口)
├── application (应用层 - 业务流程)
│ ├── service // 应用服务
│ └── port // 端口定义 (如果是严格六边形,接口定义在这里)
└── domain (领域层 - 业务核心)
├── model // 实体、聚合根
├── service // 领域服务
└── repository // 仓储接口 (定义数据该怎么存,但不关心存哪里)

区别点
你会发现 ControllerRepositoryImpl 变成了兄弟关系,都属于 Adapter(外围设施),而 Domain 被层层保护在最里面。


方案三:COLA 架构 (Clean Object-Oriented and Layered Architecture)

如果你是 Java 开发者,特别是在国内大厂环境,阿里开源的 COLA 架构 是一个非常流行的 DDD 落地模板。它结合了四层架构和整洁架构的优点。

它的特点是把对外暴露的接口(API)单独抽离成一个模块,方便微服务之间调用。

Maven 模块结构

COLA 通常是多 Module 的 Maven 项目:

  1. start (启动层)
    • Spring Boot 启动类,各种配置。
  2. adapter (适配层)
    • Controller (Web), Consumer (MQ)。
  3. client (API 层 / SDK)
    • 重点:这里放 DTO 和 API 接口定义。外部系统只需要依赖这个 jar 包。
  4. app (应用层)
    • Application Service, Command Handler。
  5. domain (领域层)
    • Entity, ValueObject, DomainService, Repository Interface。
    • 最纯净的一层,不依赖 Spring,只依赖 JDK。
  6. infrastructure (基础设施层)
    • MyBatis Mapper, Repository Impl, Redis Util。

总结:我该怎么选?

  1. 如果你是初学者,或者项目规模不大
    推荐 方案一(经典的四层架构)

    • 理由:最符合直觉,文件夹分层清晰,从 MVC 迁移过来认知负担最小。
    • 注意点:一定要坚持“领域层定义接口,基础层实现接口”,不要让领域层依赖 MyBatis。
  2. 如果你在做微服务,或者业务逻辑极度复杂
    推荐 方案二(整洁/六边形架构)

    • 理由:能够完美隔离技术实现的变动(比如从 MySQL 换到 MongoDB,核心业务代码一行都不用改)。
  3. 如果你的团队用 Java 且由于规范要求
    推荐 方案三(COLA)

    • 理由:国内资料多,规范约束性强,适合团队协作。

关键点(无论选哪个架构)

不管你选哪个文件结构,DDD 落地的生死线只有一条:

Domain 层(领域层)必须是“纯净”的。

  • 它可以引用 java.util.List
  • 绝对不能引用 javax.sql.DataSourceorg.springframework.xxxcom.github.pagehelper
  • 它只谈业务(账号、金额、冻结),不谈技术(表、JSON、HTTP)。

只要守住了这条线,你的架构就是合格的 DDD 架构。

七、总结

从MVC到Service+DAO,再到DDD,不是架构的“替代”,而是架构的“演进”。它们的核心都是通过分层实现解耦,只是针对不同的业务复杂度,选择了不同的拆分粒度。

对于开发者而言,不用盲目追求“高大上”的架构,而应该理解每种架构的核心价值和适用场景:简单业务用简单架构,复杂业务用精细架构。同时,要抓住架构设计的本质——让代码结构清晰、职责明确,便于维护和迭代。这才是我们学习各种架构思想的最终目的。