# 领域驱动设计


## 领域模型模式
目前企业级应用开发中，业务逻辑的组织方式主要是**事务脚本**模式。事务脚本按照业务处理
的过程组织业务逻辑，每个过程处理来自客户端的单个请求。客户端的每次请求都包含了一
定的业务处理逻辑，而程序则按照每次请求的业务逻辑进行划分。

事务脚本模式典型的就是 Controller→Service→Dao 这样的程序设计模式。Controller 封
装用户请求，根据请求参数构造一些数据对象调用 Service，Service 里面包含大量的业务
逻辑代码，完成对数据的处理，期间可能需要通过 Dao 从数据库中获取数据，或者将数据
写入数据库中。

**领域模型**模式和事务脚本模式不同。在领域模型模式下，业务逻辑围绕领域模型设计。

领域模型的对象则包含了对象的数据和计算逻辑

**领域模型是合并了行为和数据的领域的对
象模型**。通过领域模型对象的交互完成业务逻辑的实现，也就是说，设计好了领域模型对
象，也就设计好了业务逻辑实现。和事务脚本被称作贫血模型相对应的，领域模型也被称为
充血模型。

对于复杂的业务逻辑实现来说，用领域模型模式更有优势。特别是在持续的需求变更和业务
迭代过程中，把握好领域模型，对业务逻辑本身也会有更清晰的认识。使用领域模型增加新
的产品类型的时候，就不需要修改现有的代码，只需要扩展新的产品类和收入策略类就可以
了。

在需求变更过程中，如果一个需求和领域模型有冲突，和模型的定义以及模型间的交互逻辑
不一致，那么很有可能这个需求本身就是伪需求。很多看似合理的需求其实和业务的内在逻
辑是有冲突的，这样的需求也不会带来业务的价值，通过领域模型分析，可以识别出这样的
伪需求，使系统更好地保持一致性，也可以使开发资源投入到更有价值的地方去。

## 领域驱动设计
**领域**是一个组织所做的事情以及其包含的一切，通俗地说，就是组织的业务范围和做事方
式，也是软件开发的目标范围。比如对于淘宝这样一个以电子商务为主要业务的组织，C2C
电子商务就是它的领域。**领域驱动设计就是从领域出发，分析领域内模型及其关系，进而设
计软件系统的方法**。

如果我们说要对 C2C 电子商务这个领域进行建模设计，那么这个范围就太大了，不知
道该如何下手。所以通常的做法是把整个领域拆分成多个**子域**，比如用户、商品、订单、库
存、物流、发票等。强相关的多个子域组成一个**界限上下文**，界限上下文是对业务领域范围
的描述，对于系统实现而言，可以想象成相当于是一个子系统或者是一个模块。界限上下文
和子域共同组成组织的领域

![ddd.png](/images/ddd.png)

不同的界限上下文，也就是不同的子系统或者模块之间会有各种的交互合作。如何设计这些
交互合作呢？DDD 使用上下文映射图来完成
![context.png](/images/context.png)

在 DDD 中，领域模型对象也被称为实体，每个实体都是唯一的，具有一个唯一标识，一个
订单对象是一个实体，一个产品对象也是一个实体，订单 ID 或者产品 ID 是它们的唯一标
识。实体可能会发生变化，比如订单的状态会变化，但是它们的唯一标识不会变化。

**实体设计**是 DDD 的核心所在，首先通过业务分析，识别出实体对象，然后通过相关的业务
逻辑设计实体的属性和方法。这里最重要的，是要把握住实体的特征是什么，实体应该承担
什么职责，不应该承担什么职责，分析的时候要放在业务场景和界限上下文中，而不是想当
然地认为这样的实体就应该承担这样的角色。

事实上，并不是领域内的对象都应该被设计为实体，DDD 推荐尽可能将对象设计为值对
象。比如像住址这样的对象就是典型的**值对象**，也许建在住址上的房子可以被当做一个实
体，但是住址仅仅是对房子的一个描述，像这样仅仅用来做度量或描述的对象应该被设计为
值对象。

值对象的一个特点是不变性，一个值对象创建以后就不能再改变了。如果地址改变了，那就
是一个新地址，而一个订单实体则可能会经历创建、待支付、已支付、代发货、已发货、待
签收、待评价等各种变化。

领域实体和界限上下文包含了业务的主要逻辑，但是最终如何构建一个系统，如何将领域实
体对外暴露，开发出一个完整的系统。事实上，DDD 支持各种架构方案，比如典型的分层
架构：

![ddd_mvc.png](/images/ddd_mvc.png)

领域实体被放置在领域层，通过应用层对领域实体进行包装，最终提供一组访问接口，通过
接口层对外开放。

六边形架构是 DDD 中比较知名的一种架构方式，领域模型通过应用程序封装成一个相对比
较独立的模块，而不同的外部系统则通过不同的适配器和领域模型交互，比如可以通过
HTTP 接口访问领域模型，也可以通过 Web Service 或者消息队列访问领域模型，只需要
为这些不同的访问接口提供不同的适配器就可以了。

![ddd_hexagon.png](/images/ddd_hexagon.png)

领域驱动设计的技术体系内还有其他一些方法和概念，但是最核心的还是领域模型本身，**通
过领域实体及其交互完成业务逻辑处理才是 DDD 的核心目标**。至于是不是用了 CQRS，是
不是事件驱动，有没有事件溯源，并不是 DDD 的核心。
