领域驱动设计
领域模型模式
目前企业级应用开发中,业务逻辑的组织方式主要是事务脚本模式。事务脚本按照业务处理 的过程组织业务逻辑,每个过程处理来自客户端的单个请求。客户端的每次请求都包含了一 定的业务处理逻辑,而程序则按照每次请求的业务逻辑进行划分。
事务脚本模式典型的就是 Controller→Service→Dao 这样的程序设计模式。Controller 封 装用户请求,根据请求参数构造一些数据对象调用 Service,Service 里面包含大量的业务 逻辑代码,完成对数据的处理,期间可能需要通过 Dao 从数据库中获取数据,或者将数据 写入数据库中。
领域模型模式和事务脚本模式不同。在领域模型模式下,业务逻辑围绕领域模型设计。
领域模型的对象则包含了对象的数据和计算逻辑
领域模型是合并了行为和数据的领域的对 象模型。通过领域模型对象的交互完成业务逻辑的实现,也就是说,设计好了领域模型对 象,也就设计好了业务逻辑实现。和事务脚本被称作贫血模型相对应的,领域模型也被称为 充血模型。
对于复杂的业务逻辑实现来说,用领域模型模式更有优势。特别是在持续的需求变更和业务 迭代过程中,把握好领域模型,对业务逻辑本身也会有更清晰的认识。使用领域模型增加新 的产品类型的时候,就不需要修改现有的代码,只需要扩展新的产品类和收入策略类就可以 了。
在需求变更过程中,如果一个需求和领域模型有冲突,和模型的定义以及模型间的交互逻辑 不一致,那么很有可能这个需求本身就是伪需求。很多看似合理的需求其实和业务的内在逻 辑是有冲突的,这样的需求也不会带来业务的价值,通过领域模型分析,可以识别出这样的 伪需求,使系统更好地保持一致性,也可以使开发资源投入到更有价值的地方去。
领域驱动设计
领域是一个组织所做的事情以及其包含的一切,通俗地说,就是组织的业务范围和做事方 式,也是软件开发的目标范围。比如对于淘宝这样一个以电子商务为主要业务的组织,C2C 电子商务就是它的领域。领域驱动设计就是从领域出发,分析领域内模型及其关系,进而设 计软件系统的方法。
如果我们说要对 C2C 电子商务这个领域进行建模设计,那么这个范围就太大了,不知 道该如何下手。所以通常的做法是把整个领域拆分成多个子域,比如用户、商品、订单、库 存、物流、发票等。强相关的多个子域组成一个界限上下文,界限上下文是对业务领域范围 的描述,对于系统实现而言,可以想象成相当于是一个子系统或者是一个模块。界限上下文 和子域共同组成组织的领域

不同的界限上下文,也就是不同的子系统或者模块之间会有各种的交互合作。如何设计这些
交互合作呢?DDD 使用上下文映射图来完成

在 DDD 中,领域模型对象也被称为实体,每个实体都是唯一的,具有一个唯一标识,一个 订单对象是一个实体,一个产品对象也是一个实体,订单 ID 或者产品 ID 是它们的唯一标 识。实体可能会发生变化,比如订单的状态会变化,但是它们的唯一标识不会变化。
实体设计是 DDD 的核心所在,首先通过业务分析,识别出实体对象,然后通过相关的业务 逻辑设计实体的属性和方法。这里最重要的,是要把握住实体的特征是什么,实体应该承担 什么职责,不应该承担什么职责,分析的时候要放在业务场景和界限上下文中,而不是想当 然地认为这样的实体就应该承担这样的角色。
事实上,并不是领域内的对象都应该被设计为实体,DDD 推荐尽可能将对象设计为值对 象。比如像住址这样的对象就是典型的值对象,也许建在住址上的房子可以被当做一个实 体,但是住址仅仅是对房子的一个描述,像这样仅仅用来做度量或描述的对象应该被设计为 值对象。
值对象的一个特点是不变性,一个值对象创建以后就不能再改变了。如果地址改变了,那就 是一个新地址,而一个订单实体则可能会经历创建、待支付、已支付、代发货、已发货、待 签收、待评价等各种变化。
领域实体和界限上下文包含了业务的主要逻辑,但是最终如何构建一个系统,如何将领域实 体对外暴露,开发出一个完整的系统。事实上,DDD 支持各种架构方案,比如典型的分层 架构:

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

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