贫血模型和充血模型
-
贫血模型:指的是领域对象只包含了对象的特征,而没有对象的行为。即 POJO 中只有对象的属性和属性的 get/set 方法,所有的业务逻辑都放在业务层。
-
优点:各层次之间松耦合,结构清晰,领域对象只是用作存放和传输的载体。
-
缺点:只有属性没有行为的对象是没有生命的,这样的对象不是真正的对象,而且业务逻辑层将会十分庞大。
-
使用方式:在对象的外围构建一个 Facade 层还封装对象的某些原子操作,以此来简化 Service 层的压力,但是要注意各个模块之间的松耦合,一旦紧耦合,就失去了使用贫血模型的本意。
-
-
充血模型:是指模型中不仅包含了对象的属性,还包含了对象的行为,包括业务逻辑、数据持久化等操作。业务层则只是部分的简答调用逻辑、事务控制、权限控制等。
-
优点:面向对象开发,业务层简洁单一。
-
缺点:业务逻辑划分难以明确,什么样的业务放在对象中,什么样的业务放在业务层。模块化开发更难,业务层的人需要深入了解领域层对象的行为方法。
-
使用方法:当进行复杂业务逻辑开发的时候,可以使用充血模型来简化业务层,但是对象的行为方法一定要单一,尽量做好封装。
-
DDD中的基本概念
-
实体(Entity)
- 当一个对象由其标识(而不是属性)区分时,这种对象称为实体(Entity)。比如当两个对象的标识不同时,即使两个对象的其他属性全都相同,我们也认为他们是两个完全不同的实体。
-
值对象(Value Object)
- 当一个对象用于对事物进行描述而没有唯一标识时,那么它被称作值对象。因为在领域中并不是任何时候一个事物都需要有一个唯一的标识,也就是说我们并不关心具体是哪个事物,只关心这个事物是什么。比如下单流程中,对于配送地址来说,只要是地址信息相同,我们就认为是同一个配送地址。由于不具有唯一标示,我们也不能说这一个值对象或者那一个值对象。
-
领域服务(Domain Service)
- 一些重要的领域行为或操作,它们不太适合建模为实体对象或者值对象,它们本质上只是一些操作,并不是具体的事物,另一方面这些操作往往又会涉及到多个领域对象的操作,它们只负责来协调这些领域对象完成操作而已,那么我们可以归类它们为领域服务。它实现了全部业务逻辑并且通过各种校验手段保证业务的正确性。同时呢,它也能避免在应用层出现领域逻辑。理解起来,领域服务有点facade的味道。
-
聚合及聚合根(Aggregate,Aggregate Root)
- 聚合是通过定义领域对象之间清晰的所属关系以及边界来实现领域模型的内聚,以此来避免形成错综复杂的、难以维护的对象关系网。聚合定义了一组具有内聚关系的相关领域对象的集合,我们可以把聚合看作是一个修改数据的单元。
- 聚合根属于实体对象,它是领域对象中一个高度内聚的核心对象。(聚合根具有全局的唯一标识,而实体只有在聚合内部有唯一的本地标识,值对象没有唯一标识,不存在这个值对象或那个值对象的说法)
- 若一个聚合仅有一个实体,那这个实体就是聚合根;但要有多个实体,我们就要思考聚合内哪个对象有独立存在的意义且可以和外部领域直接进行交互。
- 例如将用户职位、用户部门、用户权限等等聚合成一个用户聚合根,聚合和仓储基本是一对一的,有一个仓储就是一个聚合
-
工厂(Factory)
- DDD中的工厂也是一种封装思想的体现。引入工厂的原因是:有时创建一个领域对象是一件相对比较复杂的事情,而不是简单的new操作。工厂的作用是隐藏创建对象的细节。事实上大部分情况下,领域对象的创建都不会相对太复杂,故我们仅需使用简单的构造函数创建对象就可以。隐藏创建对象细节的好处是显而易见的,这样就可以不会让领域层的业务逻辑泄露到应用层,同时也减轻应用层负担,它只要简单调用领域工厂来创建出期望的对象就可以了。
-
仓储(Repository)
- 资源仓储封装了基础设施来提供查询和持久化聚合操作。这样能够让我们始终关注在模型层面,把对象的存储和访问都委托给资源库来完成。它不是数据库的封装,而是领域层与基础设施之间的桥梁。DDD 关心的是领域内的模型,而不是数据库的操作。
四层架构
- Interface Layer:接口层,负责显示和接受输入;
- Application Layer(Service):应用层,很薄的一层,只包含工作流控制逻辑,不包含业务逻辑;
- Domain Layer(Domain):领域层,包含整个应用的所有业务逻辑;
- Infrastructure Layer:基础层,提供整个应用的基础服务;
数据驱动与领域驱动的区别
DDD落地
DDD落地分为三个步骤,战略设计、战术设计、代码落地
- 战略设计
战略设计主要包含限界上下文划分、映射、子域类型确定、通用语言确定- 限界上下文:业务边界划分,确定哪些是我们负责的,哪些不是我们负责的
- 映射:不同的有界上下文之间如何进行交互,例如接口调用或者MQ
- 子域类型:确定有界上下文所属子域类型,划分出核心子域、支持子域、通用子域。
- 通用语言:当前有界上下文边界范围内要制定一套统一的命名规范
- 战术设计
战术设计包含实体、值对象、聚合、仓储、领域服务、业务组件、领域事件、命令和查询- 实体:有唯一标识、而且他的数据是允许变化的,此时在ddd战术设计里,他被称之为:实体,Entity,这样的一个概念,就是一个Class类,有唯一标识,类里面的数据允许变化,例如订单状态
- 值对象:OrderId(建模成一个独立的类),对于一个订单,他的唯一的标识
- 聚合 :把有强关系的一些实体+值对象放在一块,这些实体所谓的强关系是指创建的时候一起创建,更新的时候一起更新,删除的时候一起删除,聚合里的这些实体必须放在一个事务里,要么一起成功,要么一起失败,聚合还有一些贴合业务语义的业务行为,聚合最外层的实体就叫做聚合根,聚合是要进行充血模型设计,也就是要有一些核心符合业务语义的业务逻辑行为(不依赖第三方的服务)
- 领域服务:有一些业务行为是没办法放在聚合里的,针对跨越多个聚合执行的业务行为,就没办法放在一个聚合里面来做,只能是放在领域服务里面来做, 他的业务行为都是委托给多个聚合来做
- 业务组件 :贴合业务语义的class(类),例如订单状态机、从第三方平台提供的api获取数据的行为,都可以设计成业务组件类
- 领域事件 :某个有界上下文执行的一些动作和操作会驱动其他有界上下文某个方面业务流程的执行,例如订单上下文会发起订单支付事件会驱动履约流程的执行,这就是领域事件
- 命令(查询) :人驱动发起的命令,一些核心的业务动作和操作,应该是人发起的,例如web系统网页界面、手机APP界面,通过UI界面发起的动作和操作,在DDD里面就叫做命令(command),如果是更新数据一类的动作叫命令COMMAND,如果是查询一类的动作叫查询QUERY
- 仓储 :负责与持久层进行交互,聚合和实体数据都是通过仓储来进行持久化和读取,类似于DAO
- 代码落地
DDD领域四层架构