• 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内部,借助推送消息的事务,确保聚合变化事件被压入消息队列等基础设施,从而传递到外部。
  • 相关阅读:
    「学习笔记」Min25筛
    hash索引
    Thread的setDaemon(true)方法的作用
    Reactor模式详解
    题目整理
    jstat命令查看jvm的GC情况 (以Linux为例)
    jvm参数调优
    以网游服务端的网络接入层设计为例,理解实时通信的技术挑战
    Java 工程师成神之路
    ID生成 雪花算法
  • 原文地址:https://www.cnblogs.com/Abbey/p/12786788.html
Copyright © 2020-2023  润新知