DDD Implementation Steps in FP

流程概要

Stage Process Tool Git
准备 虚拟用户角色,撰写系统描述 System Narrative git init
发现业务目标Why Impact Mapping
挖掘用例及其关系 Use Case Diagram
准备开发与部署环境 Deploy Diagram git checkout -b develop
设计 逐步找出Who(Actor)-How(Impact)-What(Feature/Deliverable) Impact Mapping
对应Impact Mapping撰写Key Specification Gherkin (Given-When-Then)
从Specification提取统一语言UL术语表 Glossary Table
尝试划分子域 Class Diagram
根据Key Specification和子域划分,编写验收测试 Spock Framework
实现 从Key Specification中提取Key Example驱动编码 Gherkin git checkout -b feature-xxx
每完成一个Key Example提交一次验收测试 Spock Framework git merge feature-xxx
回溯以上,完成全部Key Specification Live Document
实现UI Kanban
实现日志、权限管理等模块
创建数据库脚本
完成自动化集成测试 CI,Selenium git merge develop
部署 打包 Gradle/Maven git checkout -b release
发布 CI git merge release; git tag version master

编码要义

Service

根据Key Example中描绘的场景Scenario,勾勒出需要向用户提供的Domain Service(以下从编码的角度称其为领域API),以业务动作和逻辑驱动构建相应的数据结构,组建为领域ADT,由外向内进行编码实现。

辅助工具

  • Sequence Diagram
  • Data Flow Diagram
  • Gherkin

最佳实践

  • 使用trait与纯函数定义领域API模板,使用object定义API的具体实现,确保API定义与实现的脱耦。
  • 函数名称、变量名称等均采用陈述性的设计,使API成为自我阐述目的的接口。
  • 坚持以函数作为API返回值,使用模式Kleisli[M, A, B](即A=>M[B]的代数结构)进行封装,方便实现for推演。
  • 使用Try等结构面向失败建模,避免异常对业务逻辑的干扰。
  • 利用Functor/Applicative Functor/Monad模式,提高领域模型的可复用度。
  • 使用影子类型Phantom配合Kleisli模式,定义API的状态迁移,避免出现非法状态。

值对象/实体/聚合

根据领域API的需要,定义值对象。再根据生命周期不同,将部分值对象提升为实体,赋与唯一标识ID。最后根据实体与值对象之间的关系,定义聚合。

辅助工具

  • Sequence Diagram
  • Data Flow Diagram
  • Class Diagram
  • Glossary Table

最佳实践

  • 使用case class定义领域数据。
  • 使用智能构造器创建聚合实例。
  • 聚合的ID是全局的,实体的ID则仅限于聚合内部。
  • 聚合的本质是关联,坚持用ID在实体间建立单一方向的关联,在需要时才构建或者加载其实例。
  • 为聚合中每个需要向外暴露的属性定义一个Lens,以有效控制聚合的更新操作。
  • 使用State Monad管理聚合的状态改变。
  • 使用copy,避免直接暴露聚合内部的List/Map等数据结构。

Repository

根据领域API的需要,定义Repository的存Store、取Fetch和查询Query操作,为聚合提供生活的空间。

辅助工具

  • Dependence Injection
  • Class Diagram

最佳实践

  • Repository仅供Service使用,避免暴露给外部。
  • Repository通常以一次会话为其生存期。
  • 使用Reader Monad实现持久化的依赖注入。

Bounded Context与Domain Event

子域与BC没有完全的一一对应关系,所以根据区分不同服务或区分同名却不同义之领域概念的需要,定义BC。在BC内部,定义Domain Event,作为BC内部不同聚合之间通信的载体;在BC外部,定义Command和Message,作为Application Service协调各BC的载体。

最佳实践

  • 建立BC与Namespace的对应关系,实现代码的模块化。
  • 使用UTC统一所有Domain Event的时间戳。
  • 使用扁平化的数据传输对象DTO,作为Domain Event、Command和Message的载体。
  • 使用模式Free Monad,把领域行为抽象为表达式树,再定义独立的解释程序,实现早抽象、晚执行。

CQRS与Event Sourcing

采用CQRS模式,配合Event Sourcing,在Event Store支持下,确保模型在读写分离条件下实现最终一致性Eventually Consistency。

最佳实践

  • 建立快照Snapshot,提高重塑聚合的效率。
  • 每个聚合在同一时间内只能处理一条Command。
  • 在聚合变化后,借助写模型的持久化事件的事务,确保Domain Event正确存入Event Store。
  • 在Event Store内部,借助推送消息的事务,确保聚合变化事件被压入消息队列等基础设施,从而传递到外部。
posted @ 2020-04-27 15:04  没头脑的老毕  阅读(351)  评论(0编辑  收藏  举报