DDD系列:一、 Domain Primitive

为什么需要Domain Driven Design

​ 软件开发中,降低系统复杂度 是一个永恒的挑战。如使用一些设计模式或范例,从偏技术的角度来降低软件复杂度,而 DDD 的目标是对业务设计提出一种架构思想,进而降低复杂度。

Domain Primitive(DP,最原始的领域):

​ 在一个特定领域里,拥有精准定义的、可自我验证的、拥有行为的 Value Object。

  • DP是一个传统意义上的Value Object,可能拥有 Immutable 的特性*(内部的属性被构建后,不允许被修改)*
  • DP是一个完整的概念整体,拥有精准定义*(对象中属性设值是经过了校验的,是合法的)*,拥有行为
  • DP可以是业务域的最小组成部分、也可以构建复杂组合*(将业务抽象为不可继续分割的对象)*

使用 DP 需要遵循的三个原则

原则一:Make Implicit Concepts Expecit(将隐性的概念显性化)

接口的清晰度

不好的设计:
  public User register(String name, String phone, String address) 

较好的设计:
  public User register(Name, PhoneNumber, Address)

​ 当接口中同一类型参数较多时,使用时容易出错,且不易被发现。

​ 此时可以考虑将同一类型的参数转换为对应的DP模型,使接口变得更清晰、DP内的数据规范化

原则二:Make Implicit Context Expecit(让隐性的上下文显性化)

public class Money {
    private BigDecimal amount;
    private Currency currency;
    public Money(BigDecimal amount, Currency currency) {
        this.amount = amount;
        this.currency = currency;
    }
}

​ 交易过程中,Money中的currency,便是一个隐性的交易属性,将其放在Money对象中,使其凸显出来。

原则三:Encapsulate Multi-Object Behavior(封装 多对象 行为)

public void pay(Money money, Currency targetCurrency, Long recipientId) {
    ExchangeRate rate = ExchangeService.getRate(money.getCurrency(), targetCurrency);
    Money targetMoney = rate.exchange(money);
    BankService.transfer(targetMoney, recipientId);
}


public class ExchangeRate {
    private BigDecimal rate;
    private Currency from;
    private Currency to;

    public ExchangeRate(BigDecimal rate, Currency from, Currency to) {
        this.rate = rate;
        this.from = from;
        this.to = to;
    }

    public Money exchange(Money fromMoney) {
        notNull(fromMoney);
        isTrue(this.from.equals(fromMoney.getCurrency()));
        BigDecimal targetAmount = fromMoney.getAmount().multiply(rate);
        return new Money(targetAmount, to);
    }
}

​ 在跨境支付中(pay),会涉及到汇率转换,为了避免在业务逻辑中去处理汇率计算,我们用DP(ExchangeRate)包装掉“币种转换计算的”动作。计算时需要交易的双端币种、计算费率等计算属性,我们将其都放在一个DP中,封装多个对象完成计算。

常见的DP的使用场景:

  • 有格式限制的String:比如NamePhoneNumberOrderNumberZipCodeAddress等,将隐性的概念显性化
  • 有限制的Integer:比如OrderId(>0),Percentage(0-100%),Quantity(>=0)等,将隐性的概念显性化
  • 可枚举的int:比如Status,将隐性的概念显性化
  • DoubleBigDecimal:一般用到的DoubleBigDecimal都是有业务含义的,比如TemperatureMoneyAmountExchangeRateRating等,将隐性的概念显性化
  • 复杂的数据结构:比如Map<String, List<Integer>>等,尽量能把Map的所有操作包装掉,仅暴露必要行为,将多对象的行为进行封装

DP与其他POJO的区别

与Value Object的对比:

​ Domain Primitive 是 Value Object 的进阶版,在原始VO的基础上要求每个DP 拥有概念的整体,而不仅仅是值对象。在VO的Immutable基础上增加了Validity和行为。当然同样的要求无副作用(side-effect free)。

与Data Transfer Object 的区别

DTODP
功能数据传输 属于技术细节代表业务域中的概念
数据的关联只是一堆数据放在一起,不一定有关联度数据之间的高相关性
行为无行为丰富的行为和业务逻辑

按DP的思维重构老应用的流程

基于重构的原则(影响的代码范围 和 耗时),我们可以使用如下流程:

第一步:

按业务梳理出DP对象与行为,然后从老代码中抽离方法。

所有抽离出来的方法要做到无状态,因为DP属性中,不能带标识状态的数据,所以一切需要改变状态的代码都不属于DP的范畴。

第二步

替换数据校验和跟DP相关的无状态业务逻辑

为了减小该步骤的工作量,可以不修该接口的签名,只修改接口内部的实现。

第三步:

定义一个新的接口,参数使用定义的DP模型。重新编排接口实现类中的逻辑

第四步:

修改外部调用的方式(入参修改为对应的DP模型)。

参考文献:
阿里殷浩-详解DDD系列

DDD在哈啰交易中台的实践

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值