DDD 概念的解释和理解

DDD 的概念来源于 2004 年 Eric Evans 的书籍《领域驱动设计》,这本书中有大量的软件工程的概念,阅读比较困难,鉴于翻译的原因,容易望文生义。

其实 DDD 的这些概念不是 Eric 自己发明的,而是在之前就有,《领域驱动设计》这本书总结了这些概念,并发扬光大。在理解这些概念是一定需要结合实践,以及当下的环境适当的采用。

《领域驱动设计》并不需要多高的计算机科学知识,但是对软件设计的经验要求较多,尤其是业务复杂的应用软件。下面是 DDD 相关概念的总结。

1. DDD 通用类概念

DDD 是 Domain-Driven Design 的缩写。其主要的思想是,我们在设计软件时,先从业务出发,理解真实的业务含义,将业务中的一些概念吸收到软件建模中来,避免造出“大而无用”软件。也避免软件设计没有内在联系,否则一团散沙,无法继续演进。

领域(Domain)是软件要解决的业务问题和诉求。通俗来说,领域就是业务需求。不过这些业务有一些内在逻辑需要分析,存在一些专业性,比如财务、CRM、OA、电商等不同领域。计算机只是自动化的处理领域问题,领域知识可能不是开发人员擅长的。

领域模型(Model)是业务概念在程序中的一种表达方式。面向对象的编程语言使用类作为业务概念的承载体,也可以用 UML 可视化的表达模型。模型是用来设计和理解整个软件结构,切记不要事无巨细的模型。在模型思维中,模型是简单的,能反应业务概念即可。

通用语言(Ubiquitous language)是指在软件设计中,业务人员和开发人员需要使用无歧义的统一语言来对话。这些语言包括对概念的统一理解和定义,以及业务人员参与到软件建模中,否则业务的变化会造成软件巨大的变化。

模型驱动的设计和面向对象,是计算机对现实世界的映射。理解现实世界需要用到哲学的工具,比如概念的内涵和外延、本体和客体,因此 DDD 会有一点哲学的意味,需要适应。

2. 建模概念

实体(Entity)是领域模型面向对象语言的具体体现,一般是一个类。领域模型可以是一个广义的概念,建模的结果和中间过程其实都可以看做模型。

值对象 (Value Ojbect)是一种特殊的实体,但一般认为没有自己的状态和生命周期。实体可以使用 ID 标识,但是值对象是用属性标识,任何属性的变化都视为新的对象。比如一个银行账户,可以由 ID 唯一标识,币种和余额可以被修改但是还是同一个账户;交易单中的金额由币种和数值组成,无论修改哪一个属性,金额都不再是原来的金额。

聚合 (Aggregate)是一组生命周期一致的实体集合,表达统一的业务意义。 聚合的意义在于让业务统一、一致,在面向对象中有非常重要价值。

聚合根( Aggregate Root)是聚合中最核心的对象,是聚合的领航员。要管理聚合必须使用一个聚合根,然后使用聚合根来实现发现、持久化聚合的作用。聚合中有且只有一个聚合根,一个实体可以独立作为一个聚合。

界限上下文( Bounded context) 有统一业务目标和概念的聚合组成的集合。大型项目都会存在多种模型,这些模型在不同的上下文中,可能有相似业务概念,但是他们的业务目标完全不一样。识别上下文可以从概念的二义性着手,比如商品的概念在物流、交易、支付含义完全不一样,但具有不同内涵和外延,实际上他们处在不同上下文。

界限上下文可以用于微服务划分、避免模型的不正确复用带来的问题。

3. 程序架构概念

服务(Service)是领域模型的操作者,承载领域的业务规则。 模型用于承载数据和系统状态,业务规则的事情就交给服务,让逻辑更为清晰明了。

模块(Module)同领域的类或者对象组成的集合。 模块的划分没有固定的模式,如果不是特别复杂的业务逻辑可以使用上下文划分模块,次一级的包使用对象类型组织即可。

工厂(Factory)是以构建模型为职责的类或方法。工厂可以把不同的业务参数转化为领域模型,在简单的业务逻辑中可以不使用工厂,实际工作中工厂不是一个具体的概念,为了简化我们可以把静态工厂方法放到 DOT 或者其他类中。

仓库(Repository)是以持久化领域模型为职责的类。 仓库的目的是屏蔽业务逻辑和持久化基础设施的差异,由于大部分项目使用关系数据库作为存储基础设施,仓库层大多被 ORM 代为实现,如果不使用 ORM,需要自己实现仓库。

规格 (Specfication) 是一些特殊的业务规则,一般表现为计算结果是真或者假的函数。规格可以用于业务逻辑的校验、查询条件,一般来说规格是可选的,不过对于复杂的业务抽象出规格可以复用这些业务逻辑。

4. 分层设计概念

DDD 在落地时可以使用四层构架,这四层有不同的意义,必要时候可以进行裁剪。

用户界面层,向用户显示信息,处理用户输入。 对于服务端应用而言,用户界面不是指 UI,而是指接入的协议,HTTP、websocket、LDAP 等协议都属于用户界面层。

应用层,组织业务场景,编排业务,隔离场景对领域层的差异。应用层最大的用处是处理业务场景,对应面向对象的思想就是 “关注点分离”。如果是一个用户社区应用,用户面、作者面、后台管理多个面实际是多个应用,而共享一套领域代码,可以做到既能关注点分离,又能复用核心业务逻辑的目的。

领域层,实现具体的业务逻辑、规则。 实际处理业务的地方,领域层需要对应用层提供无差别的服务和能力。比如用户注册,用户可以通过邮箱自己注册,也可以由管理员在后台应用添加。注册的逻辑需要由领域层完成,但是处理用户输入参数的部分需要由应用层屏蔽。

基础设施层,提供具体的技术实现,比如存储,基础设施对业务保持透明。 如果无法对领域层提供透明的能力,说明这部分代码不适合放入基础设施层。比如,在使用 JPA 时,Repository 的 Interface 定义,不应该认为是基础设施,而 ORM 对这些 Interface 的实现和具体的 SQL 处理则可以认为是基础设施层的代码。

在使用框架时,需要弄清楚分层不是绝对的。比如,Spring MVC 的 controller,实际不是用户界面层,而是应用层,因为接入协议的已经被框架处理了。

评论已关闭