0. 有时候感觉自己在爬
淘宝官方技术账号-B乎-阿里技术专家详解DDD系列
DDD中Diff的应用(JAVERS)的封装
1. DDD概述
Domain-Driven Design 领域驱动设计
1.1 从编码角度中概述DDD
关于传统架构中service(业务处理层)的问题
数据验证、错误处理的维护逻辑冗余
接口签名的语义不够清晰
业务代码的语义不够清晰
测试覆盖性低
从编码的层面->domain primitive(DP)领域对象
实现
将隐性的概念显性化(将同一问题下的多字段作为一个新的dp引入)
将隐性的上下文显性化(将一个字段及其衍射/隐藏字段作为一个新的dp引入)
行为内聚(充血模式)
1.2 从应用架构中概述DDD
从应用的层面
domain layer(领域层):取代数据库作为最底层对象,不再依赖任何服务、框架
领域对象(domain{a,b})及其调用过程(service{return a+b;})
application layer(应用层)
编排组件(接口)的调用顺序,接口通过注入实现类
依赖domain layer
例如:Application Service、Repository、ACL
infrastructure layer(基础设施层)
组件的具体实现类
相对地来说,该层面变更的频率较低
例如:XxxMapper
ACL
Anti-Corruption Layer防腐层
ACL 不仅仅只是多了一层调用,在实际开发中ACL能够提供更多强大的功能:
适配入参、出参
缓存逻辑
(兜底)降级处理
易于测试
门禁,替代了物理的增删
2. DDD方法落地
2.1 Repository模式
Repository模式
为什么?
传统Data Mapper(DAO)属于“固件”,和底层实现(DB、Cache、文件系统等)强绑定
Aggregate root(聚合根)&一对多
聚合根对象的特征
可能引入多个Entity(领域层对象)
为何出现?
分库分表维护相关数据
牺牲易于维护的单表单表查询,带来更好的DB性能(粒度更小)
对象
DO
entity
DTO
assembler
不同对象之间的转换器
一般指DO<->entity/entity<->DTO
接口规范
接口名称不应该使用底层实现的语法
insert、select、update、delete都属于SQL语法,使用这几个词相当于和DB底层实现做了绑定
我们应该把 Repository 当成一个中性的类 似Collection 的接口,使用语法如 find、save、remove。
出参入参不应该使用底层数据格式
不应该使用DO作为参数类型
Repository 操作的是 Entity 对象(实际上应该是Aggregate Root)
避免所谓的“通用”Repository模式
借助诸如:spring jdbc、entity framework等框架的配置、注解自动实现接口
这么做的话,扩展性将缺失
值得注意的是:
Repository的接口是在Domain层,但是实现类是在Infrastructure层。
2.2 domain layer
OOP(完全的OOP)通过继承来支持扩展,违背了开闭原则,这时候自然会想到组合的扩展方式(即在domain service层组合)
这种仅凭domain primitive+domain service的做法并不能满足扩展性的需要,此时可以借鉴游戏程序的经典架构ECS:
组成模块:
Entity-Component-System
这既是其全称,也是三个组成模块的含义
将复杂的大系统拆分成独立的组件
将行为从对象代码中剥离(Moveable)
数据驱动
对象的行为由其参数决定
动态修改数据可以快速改变其行为->行为调用逻辑剥离实体(调用反转到System模块)
ECS不完全适配商业应用程序的原因:
为降低GC成本,允许直接操作数据->一致性难以保证
DDD的做法
domain primitive
尽可能的不维护id以外的数据,将非直接相关的字段隔离开来
不应该强依赖其他聚合根、领域服务
构建时必须检查所有参数是合理的
重载all args constructor
Factory、builder中通过给定一些默认值简化构建
setter建议采用行为的命名方式
通过聚合根来保证主子实体一致
任何实体的行为只能影响自身
domain service
三种常见模型:
单对象策略模型
可以采用double dispatch反转调用(将domain service作为方法参数入参到domain primitive)
跨对象事务模型(一个行为会修改多个实体)
参数来自多个实体,因此不能作为方法参数入参
需要借组一个第三方的领域服务实现
通用组件模型
domain policy
作用:封装领域规则
通过维护了两个方法:能否应用、实际业务方法
domain event
领域事件通常是立即执行的、在同一个进程内、可能是同步或异步
局限
一个问题是因为Entity不能直接依赖外部对象,所以EventBus目前只能是一个全局的Singleton(全局Singleton对象很难被单测)
3. 坑个坑
DDD架构中对象转换器自动生成框架MapStruct
比对前/后两个聚合根对象,并根据比对结果更新快照中的对象状态(吃持久态、修改、新增、移除、游离态),like jaVers
|