可落地的DDD的(2)-为什么说MVC工程架构已经过时

本文指出MVC模式虽能实现职责分离,但随业务扩展会出现逻辑混乱、代码重合度高的问题。介绍了MVC在实际应用中存在的职责分散、无边界等问题,并通过分库分表、用户等级的例子说明其弊端。提出借鉴DDD思想进行改造,在MVC纵向切分基础上增加领域横向切分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘要

mvc是一种软件设计模式,最早由Trygve Reenskaug在1978年提出,他有效的解决了表示层,控制器层,逻辑层的代码混合在一起的问题,很好的做到了职责分离。但是在实际的编码实践过程中,你会发现这个模式随着业务的扩展,变的逻辑混乱,代码重合度很高。这里提出借鉴DDD思想的一种新的工程结构

mvc的问题

通常一个前后端分离的系统,后端工程系统结构图通常下面这样

  1. 四层 controller/service/manager/mapper

  2. 不可以同级调用

  3. 上级可以知晓下级,下级不可知晓上级,也就是bean的转化放在上级
复制代码

这个分层结构职责分离是按照纵向切分的

  1. 资源服务层repository是面向DB编程

  2. service层是面向前端页面编程。
复制代码

也就是说,对于某一块的业务,他没有将逻辑抽象到一起,他只是将一次request按照纵向切分了。没有进行横向的业务切分。 这样将会导致的问题 职责分散,逻辑重复度高

  • bean的创建太随意,基本就是一个需求对应一些dto, vo,query bean
  • 不同开发者对于同一个领域的东西有不同的bean,同一个开发者对于相同逻辑的bean,过了几个月,又定义出一个差不多的bean

没有边界

  • 根本没有上下文/边界的概念,比如说店铺会和用户有交互,订单会和用户有交互,通常在DB存储时只会存关联id,然后需要去取对应的名称,其他属性信息。这些信息的获取,有些开发在manager层操作,然后将属性定义到了店铺相关的DTO中;有些放在了service层做。controller/service/manager各个层次都可以调用,没有任何约束。

mvc的演进

按照上述的说明,在一个单体服务中,随着业务的不断迭代,可能会发生什么严重的问题。

举几个真实鲜活的例子

分库分表的例子

实体A是我们业务中的一个基础的重要的实体,对应的数据表tableA,一开始业务很简单,只有1个服务,在这个服务里面调用。后来业务扩张了,有十几个服务了,然后十几个服务直接查这个tableA。tableA也扩张成为了tableA,tableB,tableC。有些人觉得代码重复度高了,将mapper/manager层拆成共通的部分打成一个jar包,然后各个微服务中引入这个jar。业务变得更加复杂了,服务扩展到几十个了,tableA数据也有几千万了,这时候要做分库分表了,怎么整。

最后花了差不多1年,涉及十几个团队,才把这个mapper/manager调用改掉,然后做分库分表。

有人可能觉得这个只要在服务拆分时,避免直接调用就可以了,那再举个其他类型的例子。

用户等级的例子

用户的等级,用户的分级是很复杂的,不同的业务阶段有这个不同的定义。比如一开始定义一个字段叫grade的代表用户等级。 然后各个业务都在查这个表的字段grade进行判断,然后产品需要改了,增加了判断必须同时要达到什么条件才能称作等级x。这时候你又得满世界的改了。

DDD的工程架构

那如何运用DDD的思想进行改造呢 核心思想:封装领域内的逻辑,统一对外暴露的入口,防止业务逻辑泄露。

  • 在mvc纵向切分的基础上,增加一层领域的横向切分
  • 同一个工程里面,领域之间的调用只能通过domainService,这样可以屏蔽领域内的数据库是如何持久化的,业务逻辑是如何判断的、算法是如何实现的。 service之间可以直接调用。
  • 领域内还是纵向切分,安装mvc分层结构。

上面的只是一个草图,我们真实的结构图比这要稍微复杂些。领域内会区分领域对象,领域服务,基础设施层。这样在领域内进行指责分离,不过从实际的执行过程中领域内的比较细节,执行起来ROI比较低,推荐大家可以先按这套执行。

画外音:估计有些程序员看到这个工程结构变化呵呵一笑,觉得没多大价值,没什么改变必要。

这种工程的结构划分从提出来的到真正被我们团队成员接受的时间周期差不多是8个月。 原因大概是这么几类

  1. 引入新的分层,太复杂了,增加了代码复杂度
  2. 我这块业务很简单,CRUD就行了,没涉及到服务之间的交互。直接mvc一条道走到黑就可以。

如果你看这篇文章也是这种感受,不妨花点时间看下你们业务的代码,看看重复度有多高,看看逻辑有多散乱。你就会明白。

DDD工程的演进

DDD工程的演进也就是服务的拆分了,放到下期讲。

总结

很多DDD的文章都在说传统的编程方式是面试数据库编程,导致对象中只有getter,setter,也就是贫血模型,贫血模型是没有业务逻辑,面向过程设计,不符合面向对象设计原则。

对于这个结论我是同意的,但是对于造成的原因不是很同意。个人认为造成这个原因的主要原因还是在于长期以来的MVC这种模式只有纵向切分导致。如果结合横向切分,有没有DDD也无所谓。这里再引用一下驱动方法不能改变任何事情这段话,如果你能深入理解职责、封装。并随着业务的迭代,不断的重构你的代码,那么你不需要什么DDD,或者其他方法论。

使用职责、封装和组合; 以接口的视角思考,即“人们如何使用我的组件?”; 使用相关技术写好代码,包括可读性、信息性、简洁、自描述,尽量避免显式地使用模式; 有能力回答特定业务的“本质”;“本质”是一个模型,但不意味着类和方法,它意味着回答问题“这个业务如何真正地工作?”

因为这些约束,都是强迫你去思考,去做职责的思考,去做模块的封装。如果你/你团队成员已经领会其中的道理并很好的运用,还需要这些条条框框干吗呢?

下一篇领域与微服务划分,欲知后事如何,请听下回分解。

相关阅读

可落地的DDD(1)-目标讨论

关注【方丈的寺院】,第一时间收到文章的更新,与方丈一起开始技术修行之路

转载于:https://juejin.im/post/5ce40ef95188252db664848a

<think>好的,我现在需要帮助用户理解DDD框架和传统MVC架构之间的主要区别。用户之前已经询问过DDD框架,现在明确提出了两者的对比需求。根据用户提供的引用内容,我需要整理出两者的核心差异,并结合之前的回答内容进行补充。 首先,回顾用户提供的引用内容,引用1到5分别提到了MVCDDD的不同点,比如关注点分离、架构层次、设计目标等。例如,引用1指出MVC注重数据、逻辑和呈现的分离,而DDD更强调领域模型和业务逻辑。引用5提到DDD通过通用语言和子领域划分来处理复杂性。这些信息需要整合到回答中。 接下来,我需要确保回答结构清晰,分点对比。根据用户之前的回答示例,通常会使用分点结构,比如核心目标、关注重点、架构层次、实施流程、适用场景和技术细节。每个点下需要明确MVCDDD的不同,并引用相关来源。 需要注意的是,用户提供的引用中有一些不完整的地方,比如引用1中DDD部分未完全显示,但结合其他引用可以推断内容。同时,引用4提到了基础层的依赖问题,这可能涉及到架构层次中的模块划分,需要提及。 此外,用户之前的回答中提到了DDD的分层架构(用户界面层、应用层、领域层、基础设施层),而MVC则是模型、视图、控制器的三层结构。这些内容需要对比,强调DDD的领域层是核心,而MVC的模型层相对简单。 还需要考虑适用场景,比如MVC适合简单或中复杂度应用,而DDD适用于业务复杂的系统。这点在引用5中提到过,DDD处理系统规模过大时的复杂性。 最后,检查是否需要补充相关问题,根据用户之前的模式,可能需要生成相关问题,如优化方法、实施挑战、如何迁移等。但用户这次的问题只要求对比区别,可能不需要额外问题,但根据用户示例可能需要添加。 总结步骤: 1. 整理用户提供的引用内容,提取关键区别点。 2. 结合之前回答的结构,分点对比核心目标、关注重点、架构层次等。 3. 确保每个对比点都有引用支持,并正确标注来源。 4. 使用清晰的中文表达,避免技术术语过于复杂。 5. 检查是否符合格式要求,如分点、引用标识等。 6. 生成相关问题,帮助用户深入理解。 现在需要确认是否有遗漏的关键点,比如统一语言、限界上下文等DDD特有的概念,以及MVC在技术实现上的简便性。同时,确保引用标记正确,例如引用1、引用2等对应到用户提供的来源。此外,注意引用4提到的模块依赖问题,可能在架构层次或技术细节部分提到。</think>#### DDD框架与MVC架构的主要区别对比 以下是两者在设计思想、架构层次、适用场景等方面的核心差异: --- ### 一、核心目标与设计思想 | **对比维度** | **MVC架构** | **DDD架构** | |---------------------|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------| | **核心目标** | **实现技术层面的关注点分离**(数据、视图、逻辑分离)[^1][^2] | **以业务领域为核心**,通过领域模型解决复杂业务问题[^3][^5] | | **设计思想** | 面向技术实现,强调代码组织和可维护性[^1] | 面向业务领域,强调领域专家与开发者通过**统一语言**协作建模[^3][^5] | | **关注重点** | 技术分层(模型-视图-控制器) | 业务边界划分(限界上下文)、领域模型抽象 | --- ### 二、架构层次与模块划分 | **对比维度** | **MVC架构** | **DDD架构** | |---------------------|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------| | **核心分层** | 三层结构:<br>1. 模型(数据逻辑)<br>2. 视图(UI展示)<br>3. 控制器(流程控制)[^2] | 四层结构:<br>1. 用户界面层<br>2. 应用层<br>3. **领域层(核心)**<br>4. 基础设施层[^3][^4] | | **领域模型地位** | 模型层通常仅为数据载体,缺乏业务逻辑封装[^1] | 领域层包含**实体、值对象、领域服务**等,直接承载业务规则[^5] | | **模块依赖关系** | 控制器直接依赖模型和视图 | 依赖倒置原则:基础设施层依赖领域层,避免技术细节污染业务逻辑[^4] | --- ### 三、实施流程与协作方式 | **对比维度** | **MVC架构** | **DDD架构** | |---------------------|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------| | **开发流程** | 以技术实现驱动,需求直接映射为代码功能[^2] | 以领域建模驱动,需先通过**事件风暴**等方式定义领域模型和限界上下文[^3][^5] | | **团队协作** | 开发者主导,业务方仅提供需求文档 | 开发者与领域专家共同参与,使用**统一语言**确保业务理解一致性[^3][^5] | | **需求响应** | 适合需求稳定的场景 | 适合业务频繁变化且复杂度高的系统 | --- ### 四、技术实现差异 | **对比维度** | **MVC架构** | **DDD架构** | |---------------------|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------| | **代码结构** | 按技术职责分层(如Controller、Service、DAO)[^2] | 按业务领域划分模块(如`order`、`payment`子域)[^3] | | **数据传递** | 直接通过DTO或ORM实体操作数据库 | 通过**仓储(Repository)**模式封装领域对象持久化,隔离技术细节[^4][^5] | | **典型问题** | 业务逻辑分散在Service层,易导致“贫血模型”[^1] | 领域模型设计成本高,需平衡领域层与技术实现[^4] | --- ### 五、适用场景对比 | **场景类型** | **MVC适用场景** | **DDD适用场景** | |---------------------|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------| | **业务复杂度** | 简单或中等复杂度(如CMS、博客系统) | 高复杂度且频繁变化(如金融交易系统、电商平台)[^3][^5] | | **团队规模** | 小型团队快速开发 | 大型团队协作,需长期维护演进 | | **技术债务风险** | 业务扩展后易出现逻辑分散、维护困难 | 通过领域模型明确业务边界,降低长期维护成本 | --- ### 六、典型案例对比 - **MVC示例(Spring MVC)**: ```java @Controller public class OrderController { @Autowired private OrderService orderService; // Service层处理业务逻辑 @PostMapping("/pay") public String payOrder(@RequestParam String orderId) { orderService.payOrder(orderId); // 业务逻辑集中在Service return "payment_success"; } } ``` 问题:`OrderService`可能逐渐膨胀为“上帝类”[^1]。 - **DDD示例(领域层设计)**: ```java public class Order { private OrderStatus status; public void markAsPaid() { if (this.status != OrderStatus.PENDING) throw new IllegalStateException("订单无法支付"); this.status = OrderStatus.PAID; // 业务规则内聚在实体中 } } ``` 优势:支付规则由领域对象自行维护,避免逻辑泄露到Service[^5]。 --- #### 总结对比表 | **维度** | **MVC** | **DDD** | |-------------------|-----------------------------------|-----------------------------------| | **核心驱动力** | 技术实现 | 业务领域 | | **代码组织** | 按技术分层 | 按业务领域划分 | | **业务逻辑位置** | 分散在Service层 | 内聚在领域层 | | **长期维护成本** | 随业务增长快速上升 | 初期高,长期低 | --- ### 相关问题 1. DDD中如何避免领域层与技术实现的耦合? 2. 如何将现有的MVC系统逐步迁移到DDD架构? 3. DDD在微服务设计中的具体实践有哪些? --- 以上对比基于以下核心差异: - **MVC**:以技术分层为中心,适合快速开发但业务复杂度低的项目[^2] - **DDD**:以领域模型为核心,通过限界上下文和统一语言应对复杂业务[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值