DDD落地实践-战术实现心得
前言
战术设计要比战略设计好理解,而且粒度针对单个微服务/上下文。关键是对聚合、富血模型、仓储本质、事件的理解。这将是全新的编码风格。
共享大聚合包的落地方案
- 大聚合包(共享jar)
- 聚合包中包含所有业务对象、值对象、业务枚举、行为接口等,它们应该是具有合理行为的富血模型
- 聚合包应该相对“纯粹”,没有杂的第三方依赖(lombok、hutool、校验注解等基本支持除外),以便被“纯净地”共享。
- 推荐的工程和package结构
- 工程:
-aggr - 包名:com.
. .aggr. - project是项目名,context是其他模块/微服务/上下文
- context包下看复杂度决定是否继续分包,比如可以有bo(业务对象)、vo(界面对象)、ao(api响应对象)等子包、info(值对象)等
- 工程:
- 不同聚合的业务实现交由其对应所属上下文工程实现
- 上下文/微服务工程
- 应用接口层 controller
- 服务层 service
- 应用/用例服务 applicationService/usecaseService
- 领域服务 domainService - 看情况简化可省略
- 其他服务
- 仓储层 repo
- 实体对象 eo/po
- 仓储接口:getById,saveOrUpdate
- 基础设施层 infrastructure
- 对象转换器(比如eo->bo,bo->vo,bo->ao)、工具类、基础实现等
- 事件和监听器 event/listener
- 应用内事件在上下文工程中即可
- 应用间事件建议放到共享大聚合
- 监听器 XxxEventListener
- 其他
- 聚合的设计
- 尽量采用小聚合,有可能一个聚合只有聚合根,大聚合不好维护
- 聚合根应该是该业务用例中关键性的业务对象
- 仓储设计
- 某些信息如只用来进行页面展示,可以用值对象,一个json文本字段即可
- 某些字段只进行页面展示但是会进行单个检索,可以单独看情况加字段
- 状态等枚举字段建议直接“见名知意”,不要用0 1 2 3..查表时看不懂
- 记住:仓储本质是高效存取数据,尽量不要附带业务逻辑痕迹到表设计里
- 仓储设计应该ORM技术无关化,上层服务层不需要知道orm实现机制,其他层也是如此,面向接口编程
- 应用/用例服务直面用户/客户/运营/系统用例,注重事务单元,且只关心输入输出
- 什么时候需要加事件
- 重要业务对象的关键“状态”发生变更时 - 里程碑事件
- 上下文之间业务关联采用异步通信时
- 上下文内降低复杂度、解耦、异步时
- 聚合和仓储
- 聚合和仓储独立设计
- 聚合中不出现仓储相关行为
- 用例服务等自行调用repo进行获取和更新
- 聚合和仓储结合
- 聚合有仓储行为,可提供抽象
- 一般搭配buildBy静态方法(or lombok开放setter和chain,慎用)
- getById,saveOrUpdate 为实例方法
- 入参是repo
- 聚合和仓储独立设计
结语
- 接近半年的DDD探索与实践告一段落
- 成果
- 理论消化
- 实际工作中实践(大需求独立上下文)
- 同行交流
- 公司分享
- 最后
- DDD坑太多了,没有做好准备,不要轻易尝试
- 理解业务很重要,技术是手段
- DDD是解决复杂业务系统痛点的手段之一,不要为了DDD而DDD,具体问题具体分析
- 自顶向下开发,面向抽象编程!