这被视为过程编程或贫血模式吗?

问题描述

假设有两个类,一个用户类,其中包含用户信息;另一个用户类。另一类是支付交易类。 场景很简单,如果用户的年龄大于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之间的差异是放置逻辑的不同地方吗?

谢谢!

解决方法

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

要回答的问题是Transaction对象中是否包含User。如果将其括起来,则意味着您始终要遍历用户记录以获取事务(并且永远不要直接访问事务)。如果事务本身具有生命周期,可以直接访问,可以控制域的其他部分,依此类推,则不能将其包含在User中,并且是完整的聚合。

transaction括在user中意味着用户拥有与交易相关的操作,因此选项2是正确的方法。

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

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

  1. API请求将发送用户年龄作为请求的一部分
  2. 控制器调用服务并以整数形式传递用户的年龄
  3. 该服务调用一个工厂方法,该方法接受age作为整数,并初始化并返回正确的交易类型。
  4. 在请求通过时,后端的用户年龄可能已更改,或者请求可能不正确。您可以通过具有“纠正政策”来解决此问题,该政策将在以后创建付款交易后运行。如果用户的年龄与所选交易类型匹配,那么一切都很好。如果没有,则交易被撤消。

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

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

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