领域驱动设计指南:事实与谬误之DDD 与 MVC
参考[*]Package by feature, not layer
[*]现代化领域驱动设计演示
[*]Eleven低成本可落地的 领域驱动设计 技术方案脚手架
本文有以下几个目的
[*]让新手少交智商税,少浪费时间看一些软文。
[*]普及一个基本概念:了解一项观点的提出年代和最初初衷,才能更好地掌握其精粹。
[*]我想指出市场上一些误人子弟的软文。
首先说明:文中所说的谬误并非原书的谬误,而是很多网上水军写的软文在不断误人子弟、传播错误认知。
MVC到底在说什么
MVC(Model-View-Controller)架构由挪威计算机科学家Trygve Mikkjel Heyerdahl Reenskaug于1979年在施乐帕克研究中心(Xerox PARC)访问期间提出。这一架构最初是为Smalltalk编程语言设计的,旨在解决图形用户界面(GUI)开发中数据管理与用户交互的复杂性问题。当时Smalltalk的GUI需要支持动态交互(如用户操作实时更新数据),传统单体架构难以维护,MVC通过解耦输入-处理-输出流程,首次实现了界面与逻辑的分离。
Reenskaug认为,GUI应用需要将不同功能模块解耦,以应对数据复杂性和用户交互的动态性。他提出将软件系统划分为三个核心组件:
[*]模型(Model) :封装数据和业务逻辑,独立于界面展示,例如数据库结构或业务规则。
[*]视图(View):负责用户界面的呈现,直接与用户交互,例如窗口、按钮等可视化元素。
[*]控制器(Controller):协调模型与视图的交互,处理用户输入并更新模型状态,例如按钮点击后的逻辑判断。
MVC的Model本身包含基础业务逻辑(如数据验证),但复杂业务场景下需独立的应用逻辑层(如Service层)来组织流程,这与DDD的领域建模形成互补。因此,四层架构(Model-View-Controller-Service)的出现是企业级开发的演进,而非MVC原生缺陷。
DDD到底在说什么
DDD由Eric Evans 在2003年出版的经典著作《领域驱动设计:软件核心复杂性应对之道》中系统提出。其诞生源于对复杂业务系统开发困境的反思:
[*]传统开发的痛点:
[*]软件模型与真实业务领域脱节,导致需求频繁变更时难以维护;
[*]技术团队与领域专家(如业务分析师、行业专家)沟通低效,术语不统一,模型设计偏离实际业务逻辑;
[*]当业务复杂度高(如金融、供应链、医疗等领域)时,传统开发方法(如数据驱动设计、贫血模型)无法有效管理复杂性,代码逐渐沦为"意大利面条"。
[*]核心目标 :
Evans认为,应对复杂业务系统的关键在于将领域知识作为设计的核心,通过建立清晰、准确的领域模型,让技术实现紧密贴合业务本质,从而提升系统的可维护性和扩展性。
[*]
[*]战略设计:通过限界上下文(Bounded Context)划分业务边界,明确领域模型的适用范围(如电商中的"订单域"与"支付域"),解决业务与技术对齐问题;
[*]战术设计:通过实体、值对象、聚合根等工具实现领域模型,确保业务规则封装在代码中。
DDD与MVC并不冲突
在传统MVC架构下,解决GUI问题时,我们会设计GUI层面的技术模型,再根据模型渲染界面。同理,解决业务逻辑问题时,也可以设计一个领域模型,再基于模型开发业务逻辑。
从图中不难看出:领域驱动设计的核心是教你如何设计业务逻辑, 注意,是"业务逻辑设计",而非技术分层设计。原因很简单:DDD原书明确指出,这不是一本教你写代码的书,而是教你如何应对复杂软件的方法论。
无论哪个层面的技术开发,都可以先建模,再基于模型开发, 这是几乎所有行业都在使用的通用手段。
DDD本来就不存在统一的代码规范,原书也未给出具体实现手段
回到上图,你会发现:任何一个技术维度的修改,都不需要其他维度的直接支持,甚至可以单独调整某个维度------这正是DDD在战术设计上想表达的理念。但这部分内容被放在原书的最后章节,不仅因为前面的章节是前提,更因为代码架构并非DDD的核心。
DDD的核心是什么?
[*]统一语言:团队(包括业务专家)使用一致的术语描述业务规则(如"订单已支付"对应领域事件);
[*]领域模型:围绕业务概念设计代码,而非围绕数据库或技术框架;
[*]解耦思想:通过聚合根、仓储等模式隔离业务逻辑与技术细节。
DDD 的本质是什么?
DDD 本质上是一种思想哲学与行为准则,而非可直接套用的代码模板。它要求开发者从 “技术驱动” 转向 “业务驱动”,就像面向对象设计中的 “接口定义契约”——DDD 定义了 “如何将业务知识转化为有效模型” 的抽象原则(如建立统一语言、识别领域边界、封装业务规则),但具体的实现路径需要团队结合业务特性与技术栈自主设计。例如,统一语言的落地需要团队共同梳理领域术语表,领域模型的构建需要分析业务实体间的依赖关系,这些都不是 “照搬代码模板” 就能完成的,而是需要通过持续的领域分析、团队协作和架构演进才能实现。这种思想上的转变,如同在编程中从 “关注具体实现” 到 “关注抽象契约” 的升华 ——DDD 提供的是 “做什么” 的方向,而非 “怎么做” 的标准答案,最终的架构形态需要开发者像实现接口一样,用代码去 “填充” 这些思想准则的具体内涵。
代码规范的真相是什么?
DDD 并不强制规定具体代码结构和命名,但业界基于实践形成了通用分层原则(如四层架构),其核心目的是通过明确的层次隔离,让不同职责的代码像 “分装进不同包装袋的商品”—— 领域逻辑不泄露到技术层,技术细节不污染业务核心,确保各层 “各司其职”。例如:
[*]领域层(Domain Layer):如同 “商品核心本体”,封装最纯粹的业务规则(如订单的支付逻辑、库存扣减规则),不依赖任何技术框架,就像商品本身的品质不受包装影响;
[*]应用层(Application Layer):类似 “商品分拣台”,协调领域对象完成跨模块操作(如调用订单领域的支付()方法后,触发库存领域的扣减库存()方法),同时处理事务、权限等非业务核心逻辑;
[*]基础设施层(Infrastructure Layer):好比 “运输工具”,负责数据库交互、消息队列、文件存储等技术实现,为上层提供 “通用工具”,但不介入业务规则判断。
与其说这是一种分层,倒不如是一种思想哲学,其实本质上不一定需要让你的“包”完全符合这种分层吗,只要我们的代码结构能体现出这些哲学思想在里面就行,这是让团队在统一的哲学思想(如 “业务逻辑优先于技术实现”)下形成可重复的开发套路 —— 正如软件开发成熟度模型(CMMI)的第二层 “可重复级” 所要求的:团队拥有明确且一致的规范,而非依赖个别高手的经验。当所有开发者对 “哪些代码该放在领域层,哪些该放在应用层” 达成共识时,代码就具备了 “自解释性”,新人能快速理解架构脉络,维护者能精准定位修改点,这正是工程化协作的基石。 至于分包方式(按功能如order/ user/,或按技术层如controller/ service/),本质是对 OOP 最佳实践(单一职责、接口隔离、依赖倒置)的具体落地:
[*]按功能分包遵循 “高内聚” 原则,将同一业务模块(如订单)的领域模型、应用服务、接口适配统一收纳,避免跨模块逻辑混杂;
[*]按技术层分包符合 “关注点分离”,让开发者快速定位某类技术角色(如所有控制器集中在controller/包),适合技术栈单一的中小型项目。
无论采用哪种方式,核心是通过分层与分包建立 “显性规则”—— 就像包装袋上的标签,让代码的 “职责边界” 一目了然。DDD 的价值不在于规定具体的包名或类名,而在于引导团队建立 “先定义层次契约,再填充具体实现” 的工程思维,让规范成为团队协作的 “共同语言”,而非束缚创造力的教条。争议与选择
业界关于代码结构的争议核心是 “按功能分包”(如order/ user/)与 “按技术层分包”(如controller/ service/)的选择:
[*]按功能分包:聚焦业务模块隔离,适合大型复杂系统(如电商的订单、支付模块独立开发),但技术复用需额外设计;
[*]按技术层分包:便于技术栈管理,适合中小型项目(如 Spring Boot 默认分层快速落地),但业务逻辑易混杂。
两者无绝对优劣,大型项目常需混合模式(如模块内保留技术分层,跨模块按业务隔离),核心是结合团队规模与业务复杂度。但无论何种方式,先对业务建模分析,建立团队统一的技术规范,始终是架构设计的前提 —— 就像建房先定功能分区,再选建材,规范比单一模式更重要。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]