0

假设有两个类,一个是用户类,其中包含用户信息;另一个是支付交易类。场景很简单,如果用户年龄>65岁,创建A类支付交易;否则,创建 B 类支付交易。

做这件事有很多种方法:

  1. 创建一个既不属于用户也不属于事务的方法,只需调用 CreateTransaction。此方法中说明了逻辑:
    func CreateTransaction(user, transaction) {
        if user.GetAge() > 65:
            transaction.CreateA()
        else:
            transaction.CreateB()
    }
  1. 另一个选项是为用户类创建方法:
     class User {
        ...
        func CreateTransaction(transaction) {
            if user.GetAge() > 65:
                transaction.CreateA()
            else:
                transaction.CreateB()
        }
     }

然后有一个 CreateTransactionController 方法调用该函数,如:

func CreateTransactinController(user, transaction) {
    user.CreateTransaction()
}

我的问题是,选项 1 是否被视为过程编程,因为逻辑实际上不属于任何对象?(或贫血模式?) 1 和 2 之间的区别是否只是放置逻辑的不同位置?

谢谢!

4

1 回答 1

1

由于您将此问题标记为 DDD,因此我将回答由域驱动的模型将如何实现此问题。

要回答的问题是 aTransaction是否包含在User对象中。如果它是封闭的,则意味着您总是通过用户的记录来获取交易(并且从不直接访问交易)。如果一个事务本身有生命周期,可以直接访问,控制领域的其他部分等等,它就不能包含在 a 中User并且是一个成熟的聚合。

将括起来transaction意味着user用户拥有与事务相关的操作,因此选项 2 将是正确的方法。

如果transaction是不同的聚合,您可以使用 a Domain Service(如您的选项 1),但这是不正确的,因为您同时处理两个聚合(usertransaction)。您最好将此功能包含在Transaction聚合中。

下一个要解决的问题是您将如何决定交易的类型。一种方法是:

  1. API 请求会将用户的年龄作为请求的一部分发送
  2. 控制器调用服务并将用户的年龄作为整数传递
  3. 该服务调用一个工厂方法,该方法接受年龄为整数,初始化并返回正确的事务类型。
  4. 在请求通过时,用户的年龄可能在后端发生了变化,或者请求可能不正确。您可以通过在稍后创建支付交易后运行的“纠正策略”来处理此问题。如果用户的年龄与选择的交易类型相匹配,那么一切都很好。如果没有,则交易被撤销。

这通常是您处理依赖于来自多个聚合的属性的更改的方式。您继续更改系统中聚合的状态,但在稍后的时间点检查相关的聚合数据,如果事情不一致,则撤消更改。

更好的方法是创建一个Specification明确的任务是根据用户的年龄得出正确的支付类型。该规范包含您的业务逻辑(> 65),为年龄驱动的需求提供上下文,并充当您控制逻辑的中心位置。

您可以在此处阅读有关规格的更多信息:https ://martinfowler.com/apsupp/spec.pdf

于 2020-10-08T15:11:26.083 回答