DDD在项目中能落地吗?在CQRS和Event Sourcing之外还有什么设计方法?InMemory和 InDatabase架构风格是什么?
InMemory架构风格
DDD领域驱动开发是一种In Memory架构风格,Memory架构风格的意思就是以计算机“内存”作为主要资源进行软件设计的方式,与之对应的就是Database数据库架构风格,典型的是以ER图(实体关系图)作为主要设计方式的设计方法。
| 架构风格 | 特点 |
|---|---|
| Memory架构风格 | 以面向对象设计为基础,针对业务对象、数据实体或业务行为等进行业务建模,主要采用设计模式、DDD等设计方法 |
| Database架构风格 | 以数据关系设计为基础,针对业务过程中的数据结构和表关系等进行数据建模,主要采用ER图、关系数据库表等设计方法 |
DDD中的CQRS和Event Sourcing就是两种Memory架构风格的设计方法。
贫血模型VS富血模型
Memory架构设计风格中,最典型的冲突点就是贫血模型与富血模型,
| 模型 | 描述 |
|---|---|
| 贫血模型 | 强调符合JavaBean规范,Java数据对象符合JavaBean规范,业务行为放到Service类中实现,导致模型类只包含setter/getter无业务行为 |
| 富血模型 | 强调面向对象设计方法,拿人作为类比,人是具有属性和行为的复合体,所以模型类应该既包含数据又包含业务行为 |
绝大多数的项目采用的都是“贫血模型”,在这个争论点提出来后,我们也怀疑过贫血模型是否是个问题,但是在项目实践中将数据与行为同时放到一个对象(类)中,会导致非常难于维护的代码和坏味道。甚至为了解决这个冲突点出现过动态Mix类属性和行为的方法,不过难以使用。
在经过长期实践后发现贫血或富血并不是一个问题,而是随着软件业务的复杂性提升和计算机硬件的成本降低,架构设计的长期趋势已经从数据库为中心转换为以内存业务模型为中心的设计。
MEES架构设计方法
MEES: Model Enhance Event Service。模型增强事件服务架构。 MEES是一种类似CQRS和EventSourcing并且基于InMemory架构风格的架构模式,该架构模式的设计视图如下。
MEES架构设计过程:
- 基于业务中抽象领域模型,定义为Model模型
- 每个Model模型对应单独ModelDecorator,装饰器类承载模型对象的业务逻辑行为
- 领域Gateway通过Model模型调用Decorator进行业务处理
- Event事件处理后通过发布订阅Pub/Sub方式通知相关Model模型进行状态State更新
- 通过Service实现技术功能逻辑,实现业务逻辑和技术逻辑的简单解耦,比如Service可以调用Database实现数据的持久化管理
基于DSL的MEES设计过程
MEES设计方法特别适用于使用DSL领域特定语言的开发场景,Dockerfile可以认为是一种DSL,下面用Dockerfile举例MEES架构设计过程。
任何业务如果可以使用一种DSL语言描述,都适用于此架构设计方法。
DSL业务定义语言
FROM centos
ENV NODE_VERSION 7.2.0
RUN yum -y install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
RUN echo 'hello world'
模型Model设计
| 模型 | 类 | 职责 |
|---|---|---|
| 领域Gateway | Dockerfile | 领域聚合根,命令执行入口 |
| 实体模型 | From | 模型定义 |
| 实体模型 | Run | 模型定义 |
| 实体模型 | Env | 定义 |
增强模型Decorator设计
| 类 | 装饰器 | 主要业务行为 |
|---|---|---|
| From | FromDecorator | 获取中央仓库镜像 |
| Run | RunDecorator | 执行参数本地命令 |
| Env | EnvDecorator | 设置全局参数 |
事件Event设计
依据Dockerfile的业务执行过程,排除模型Build过程外,可以抽象如下几个事件用于触发模型运行。
| 事件 | 业务行为 |
|---|---|
| RunEvent | 模拟docker命令运行dockerfile |
| StateEvent | 获取容器运行状态 |
服务Service设计
| 服务 | 类 | 技术行为 |
|---|---|---|
| 命令服务 | ShellService | 用于执行OS命令 |
| 仓库服务 | RepoService | 用于HTTP加载远程镜像 |
| 消息服务 | MsgService | 用于消息的发布订阅Pub/Sub |
架构设计视图View
基于以上的设计过程,MEES开发架构定义如下
示例代码
MEES架构设计产生的代码样例如下,通过Builder模式加载DSL实例后,首先通过将Event传递给领域Gateway触发模型运行,然后Model模型通过注册表Registry查询对应Decorator,再次通过Decorator实现业务层面逻辑,最后调用Service实现技术功能层面逻辑。
var docker = DockerfileBuilder.builder(file).build(){
from:Model
env:List<Model>
run:List<Model>
registry:List<ModelDecorator>
init(){
from.subscribe(DownloadedMessage){
for(r: run){
var deco = registry.findDeco(r)
deco.execute(this){
shellService.execute(cmd)
}
}
}
}
}
docker.handle(e:StateEvent){
return this.run
}
docker.handle(e:RunEvent){
var deco = registry.findDeco(this.from)
deco.download(){
repoService.download(from)
if(downloaded){
message.publish(DownloadedMessage())
}
}
}
总结
| DDD方案 | 技术方案特点 |
|---|---|
| MEES | 基于结构模式,基于Model定义Decorator增强模型方案 |
| CQRS | 基于行为模式,一般采用管道过滤器技术架构 |
| EventSourcing | 基于数据模式,一般是定义事件数据链结构 |