美文网首页js css html
DDD笔记2:战术设计(交易域)

DDD笔记2:战术设计(交易域)

作者: 王侦 | 来源:发表于2023-03-19 10:08 被阅读0次

    1.环境准备、开发约定和架构说明



    2.战术设计分析和交易域依赖准备工作

    2.1 交易域业务流程熟悉

    2.2 针对交易域进行战术设计分析

    之前的陈述法关注于:角色和活动。

    还需要分析对象以及对象之间的关联,才能写代码。

    对象间关系

    • 一个对象为另一个对象的状态变更提供数据
    • 一个对象状态变更会导致另一个对象的状态变更

    2.3 核心域上下文的依赖准备工作

    这里只能展示静态的关系图,对于复杂的时序图,需要使用UML工具。


    类图的限制

    • 没法描述跨越上下文的关系,没法应对复杂系统
    • 类的细节会发生变化

    上下文交互图:

    • 各个上下文提供的接口

    3.实体和值对象

    3.1 什么是实体和值对象

    实体:主要由标识定义的对象被称作Entity

    值对象:用于描述领域的某个方面而本身没有概念标识的对象称为值对象。

    需要以ID来跟踪状态变化的对象为实体,否则为值对象。

    对象的相等性

    • 1)标识符相等性,id(实体)
    • 2)引用(指针)相等性
    • 3)属性相等性(值对象)

    3.2 为什么要区分实体和值对象

    原因

    • 值对象往往更轻量级
    • 值对象不用跟踪变化
    • 实体和值对象在领域中扮演的角色不一样

    尽可能用值对象而不是实体。

    3.3 如何区分实体和值对象

    方法:

    • 根据上面描述的技术特征
    • 更直观的领域模型特点
      是否只读;
      生命周期是否跨越活动;

    值对象:

    • VendingMachineCommodityList售卖机商品列表

    实体:

    • Order订单

    4.领域对象的构造

    4.1 领域对象构造的问题

    问题:

    • 对象构造是谁的职责,如何确保相关对象的一致性
    • 如何兼顾对象构造的简便性和对象的封装性(Builder模式)
    • 实体ID怎么生成

    4.2 领域对象构造的几种方法

    两种模式

    • 工厂方法模式
    • 抽象工厂模式

    货道售卖机与订单之间是聚合关系,货道售卖机是聚合根。

    货道售卖机创建订单。

    4.3 实体ID的生成方式

    三种方法

    • 基于已有信息的拼接
    • 基于数据库表自增ID
    • 基于独立的ID生成器

    5.资源库与持久化

    5.1 什么是资源库

    为每种需要全局访问的对类型创建一个对象,这个对象相当于该类型所有对象在内存中一个集合的“替身”。通过一个众所周知的全局接口来提供访问。

    带必要管理功能的领域对象容器,与技术实现无关。

    5.2 资源库的意义

    提供一个管理领域对象的简单模型。

    使领域模型和持久化技术解耦。

    5.3 资源库的实现

    OrderRepositoryImpl -> OrderMapper
    放到适配层。

    6.优雅地实现一致性:聚合

    6.1 什么是聚合和聚合根

    聚合就是一组相关对象的集合,把它作为数据修改的单元。每个聚合都有一个根和一个边界。聚合根是聚合锁包含的一个特定实体。对聚合而言,外部对象只可以引用根,而边界内部的对象之间则可以相互引用。

    聚合是拥有事务一致性(强一致性)的领域对象组合。

    聚合的原则

    • 聚合内的实体适用事务一致性
    • 聚合之间适用最终一致性
    • 不脱离聚合根修改聚合内部对象
    • 聚合根有全局唯一标识,聚合内部实体只有局部标识
    • 聚合根可以从资源库获取,聚合内部实体不能

    6.2 聚合解决什么问题

    解决的问题

    • 优雅实现一致性
    • 聚合是上下文粒度的下限

    分布式事务

    • 最佳解决方案就是不要使用
    • CAP原则,如果满足CP就会牺牲A

    6.3 如何识别出聚合

    实体是否在所有活动中都协同变更。

    货道售卖机与订单是协同变更。

    支付是外部平台,可以牺牲掉一部分支付的一致性换取可用性。

    6.4 聚合的实现

    SlotVendingMachine与Order。

    保证两个操作的原子性,使用乐观锁。

    7.独立的业务逻辑:领域服务

    当领域中某个重要的过程或者转换操作不是实体或值对象的自然职责时,应该在模型中添加一个作为独立接口的操作,并将其声明为领域服务。定义接口时要使用模型语言,并确保操作名称是通用语言中的术语。此外,应该使领域服务称为无状态的。

    三个关键点

    • 业务逻辑(业务无关的代码不能放在这里,比如事务管理,访问授权,数据格式转换等不能放在这里)
    • 不属于实体或值对象
    • 无状态

    AppTradeService

    • onCabineLocked对于新用户打折
    • 新增ActivityService领域服务对于新用户打折

    领域服务的模糊性

    • 仅在必要时引入领域服务
    • 领域服务(仅包含业务逻辑)与应用层服务(原则上不包含任何业务逻辑)的区别

    8.应用层实现

    8.1 应用层的定义和职责

    定义软件要完成的任务,并且指挥表达领域概念的对象来解决问题。这一层所负责的工作对业务来说意义重大,也是与其他系统的应用层进行交互的必要渠道。应用层要尽量简单,不包含业务规则或者知识,而只为下一层中的领域对象协调任务,分配工作,使他们互相协作。

    应用层是调用领域模型完成用户需求的地方。

    应用层的职责

    • 事务控制
    • 身份认证和访问权限 Spring Security和JWT
    • 定时任务调度 Quartz
    • 事件订阅
      事件监听:适配层
      事件处理:应用层

    8.2 应用层的实现方法

    8.2.1 事务 + 任务调度

    AppTradeServcie

    • 事务失效:自调用方法,方法不是public
    • 调度TradeExpireExecutor

    RetryExecutorBase

    • executeInternal不是public,事务不能加注解,可以手动控制

    TradeExpireExecutor

    8.2.2 身份认证和访问权限(鉴权)

    8.3 应用服务和领域服务的区别

    应用服务:所有协调性的工作,不包含业务逻辑。
    领域服务:只包含无处安放(不能放到实体和值对象)的业务逻辑。

    9.适配层

    事件监听器

    参考

    相关文章

      网友评论

        本文标题:DDD笔记2:战术设计(交易域)

        本文链接:https://www.haomeiwen.com/subject/kupsrdtx.html