领域驱动的事实与谬误 一 DDD 与 MVC
本文有以下几个目的:
- 让新手少交智商税,少浪费时间看一些软文。
- 普及一个基本概念:了解一项观点的提出年代和最初初衷,才能更好地掌握其精粹。
- 我想指出市场上一些误人子弟的软文。
首先说明:文中所说的谬误并非原书的谬误,而是很多网上水军写的软文在不断误人子弟、传播错误认知。
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认为,应对复杂业务系统的关键在于将领域知识作为设计的核心,通过建立清晰、准确的领域模型,让技术实现紧密贴合业务本质,从而提升系统的可维护性和扩展性。
DDD分为两个层面:
- 战略设计:通过限界上下文(Bounded Context)划分业务边界,明确领域模型的适用范围(如电商中的"订单域"与"支付域"),解决业务与技术对齐问题;
- 战术设计:通过实体、值对象、聚合根等工具实现领域模型,确保业务规则封装在代码中。
DDD与MVC并不冲突
在传统MVC架构下,解决GUI问题时,我们会设计GUI层面的技术模型,再根据模型渲染界面。同理,解决业务逻辑问题时,也可以设计一个领域模型,再基于模型开发业务逻辑。
参考下图:

从图中不难看出:领域驱动设计的核心是教你如何设计业务逻辑------注意,是"业务逻辑设计",而非技术分层设计。原因很简单:DDD原书明确指出,这不是一本教你写代码的书,而是教你如何应对复杂软件的方法论。
通用原则 :无论哪个层面的技术开发,都可以先建模,再基于模型开发------这是几乎所有行业都在使用的通用手段。
DDD本来就不存在统一的代码规范,原书也未给出具体实现手段
回到上图,你会发现:任何一个技术维度的修改,都不需要其他维度的直接支持,甚至可以单独调整某个维度------这正是DDD在战术设计上想表达的理念。但这部分内容被放在原书的最后章节,不仅因为前面的章节是前提,更因为代码架构并非DDD的核心。
DDD的核心是什么?
- 统一语言:团队(包括业务专家)使用一致的术语描述业务规则(如"订单已支付"对应领域事件);
- 领域模型:围绕业务概念设计代码,而非围绕数据库或技术框架;
- 解耦思想:通过聚合根、仓储等模式隔离业务逻辑与技术细节。
代码规范的真相 :
DDD不强制规定具体代码结构和命名,但业界基于实践形成了通用分层原则(如四层架构:表现层、应用层、领域层、基础设施层)。例如:
- 领域层(Domain Layer)封装核心业务逻辑,包含实体、值对象、聚合根;
- 应用层(Application Layer)协调领域对象,处理事务和权限;
- 基础设施层(Infrastructure Layer)处理数据库、消息队列等技术细节。
争议与选择 :
业界关于代码结构的最大争议是按功能分包 vs 按技术层分包:
- 按功能分包(如
order/user/)利于业务模块隔离,适合大型复杂系统; - 按技术层分包(如
controller/service/)便于技术栈管理,适合中小型项目。 两者无绝对优劣,需结合团队规模和业务复杂度选择, 但是无论如何每一个项目团队都应该做的,就是对业务进行建模分析,对团队开发形成整齐划一的技术规范。
附录一些可以参考的代码和技术文章
领域驱动的事实与谬误 一 DDD 与 MVC的更多相关文章
- [转]DDD领域驱动设计基本理论知识总结
领域驱动设计之领域模型 加一个导航,关于如何设计聚合的详细思考,见这篇文章. 2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity i ...
- DDD领域驱动设计基本理论知识总结
领域驱动设计之领域模型 加一个导航,关于如何设计聚合的详细思考,见这篇文章. 2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity i ...
- 分享我对领域驱动设计(DDD)的学习成果
本文内容提要: 1. 领域驱动设计之领域模型 2. 为什么建立一个领域模型是重要的 3. 领域通用语言(Ubiquitous Language) 4.将领域模型转换为代码实现的最佳实践 5. 领域建模 ...
- DDD领域驱动设计和实践(转载)
-->目录导航 一. DDD领域驱动设计介绍 1. 什么是领域驱动设计(DDD) 2. 领域驱动设计的特点 3. 如果不使用DDD? 4. 领域驱动设计的分层架构和构成要素 5. 事务脚本和领域 ...
- DDD(Domain Driver Designer) 领域驱动设计简介
领域驱动设计之领域模型 加一个导航,关于如何设计聚合的详细思考,见这篇文章. 2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity i ...
- 我对领域驱动设计(DDD)的学习成果
领域驱动设计之领域模型 2004年Eric Evans发表Domain-Driven Design – Tackling Complexity in the Heart of Software (领域 ...
- DDD领域驱动设计基本理论知识总结(转)
领域驱动设计之领域模型 为什么建立一个领域模型是重要的 领域通用语言(UBIQUITOUS LANGUAGE) 将领域模型转换为代码实现的最佳实践 领域建模时思考问题的角度 领域驱动设计的经典分层架构 ...
- 【我们一起写框架】领域驱动设计的CodeFirst框架(一)—序篇
前言 领域驱动设计,其实已经是一个很古老的概念了,但它的复杂度依旧让学习的人头疼不已. 互联网关于领域驱动的文章有很多,每一篇写的都很好,理解领域驱动设计的人都看的懂. 不过,这些文章对于那些初学者而 ...
- .net core +codefirst(.net core 基础入门,适合这方面的小白阅读) 【我们一起写框架】领域驱动设计的CodeFirst框架(一)—序篇
.net core +codefirst(.net core 基础入门,适合这方面的小白阅读) 前言 .net core mvc和 .net mvc开发很相似,比如 视图-模型-控制器结构.所以. ...
- 基于ABP落地领域驱动设计-02.聚合和聚合根的最佳实践和原则
目录 前言 聚合 聚合和聚合根原则 包含业务原则 单个单元原则 事务边界原则 可序列化原则 聚合和聚合根最佳实践 只通过ID引用其他聚合 用于 EF Core 和 关系型数据库 保持聚合根足够小 聚合 ...
随机推荐
- dp 常见套路总结
dp 里存的东西值域不大的时候,考虑把状态中某一维和 dp 里存的东西交换,进行 dp. 连续段 dp 时,考虑把连续段化为对每个元素考虑接上一个元素. dp 里的值可能存在某个上界,超过这个值一定不 ...
- FreeSql学习笔记——8.数据返回类型
前言 FreeSql数据返回格式比较丰富,包括单条.列表.导航属性数据.指定字段.Dto等:可以有效的减少代码量,减少字段复制等操作: 前面的查询已经用到了日常基本需要用到的数据格式,本篇是常 ...
- MinIO笔记
MinIO (网站 https://min.io/) 是开源的对象存储项目, 用Go实现, 支持Linux环境, 客户端支Java,Python,Javacript, Go等语言. 在分布式项目中, ...
- Python构建包、上传包详细步骤
1.从git上拉取最新的代码 2.在当前项目目录中创建setup.py文件 setup.py 1 # coding: utf-8 2 3 """打包 4 " ...
- Ruoyi-vue 左侧菜单栏默认保持收缩
在项目的 src\store\modules\app.js 路径下 sidebar: { opened: Cookies.get('sidebarStatus') ? !!+Cookies.get(' ...
- P5355 [Ynoi Easy Round 2017] 由乃的玉米田
莫队 + bitset + 根号分支 乘法似乎是简单的,我们可以直接莫队扫描然后枚举较小数 时间 \((n + m) \sqrt n\). 加法是一个经典 idea, 莫队套 bitset,然后利用 ...
- 解密注意力机制:为什么Flash Attention这么屌?
背景回顾:什么是大语言模型(LLM)? 在进入注意力机制的细节之前,我们先了解一下什么是大语言模型(LLM).简单来说,LLM是一种通过深度学习技术训练的大规模神经网络模型,用于处理和生成自然语言.L ...
- c# virtual 关键字 虚方法
1.简单的说,虚方法就是可以被子类重写的方法,如果子类重写了虚方法,那么运行时将使用重写后的逻辑,如果没有重写,则使用父类中虚方法的逻辑 class Program { static void Mai ...
- 解决macOS无法验证“xxx”的开发者。你确定要打开它吗?
前言 当 macOS 无法验证开发者时,有两种方式解决,可以通过以下步骤来打开 xxx 系统偏好设置: 打开"系统偏好设置". 选择"安全性与隐私". 在&qu ...
- Prometheus Go client library 详解
介绍 Prometheus 支持 4 种 指标类型,分别是 Counter.Gauge.Histogram 和 Summary. Counter 指标类型,指标值是只能递增,不能递减的数值.需要注意的 ...