假设有两个类,一个是用户类,它包含用户信息;另一个是支付交易类。如果用户的年龄>65、创建A类支付交易;否则,创建B类支付交易。
有几种方法可以做到这一点:
- 创建一个既不属于用户也不属于事务的方法,只需调用CreateTransaction即可。该方法的逻辑如下:
func CreateTransaction(user, transaction) {
if user.GetAge() > 65:
transaction.CreateA()
else:
transaction.CreateB()
}
- 另一个选项是为用户类创建一个方法:
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,我将回答由Domain驱动的模型将如何实现此问题。
要回答的问题是Transaction
是否包含在User
对象中。如果它是封闭的,这意味着您总是通过用户的记录来获取事务(而从不直接访问事务(。如果事务本身有生命周期,可以直接访问,控制域的其他部分,等等,那么它就不能包含在User
中,而是一个完整的聚合。
将transaction
封装在user
中意味着用户拥有与事务相关的操作,因此选项2将是正确的方法。
如果transaction
是不同的聚合,则可以使用Domain Service
(与选项1类似(,但这是不正确的,因为您同时处理两个聚合(user
和transaction
(。您最好将此功能包含在Transaction
聚合中。
下一个需要解决的问题是如何决定交易类型。一种方法是:
- API请求会将用户年龄作为请求的一部分发送
- 控制器调用服务并将用户的年龄作为整数传递
- 该服务调用一个工厂方法,该方法接受age作为整数,初始化并返回正确类型的事务
- 当请求通过时,用户的年龄可能在后端发生了变化,或者请求可能不正确。您通过使用";纠正政策;其在稍后创建支付交易之后运行。如果用户的年龄与所选择的交易类型相匹配,那么一切都是好的。如果没有,则交易被撤销
这通常是处理依赖于多个聚合属性的更改的方式。您继续更改系统中聚合的状态,但在稍后的时间点检查相关聚合数据,如果不一致,则反转更改。
更好的方法是创建一个Specification
,其明确任务是根据用户的年龄导出正确的支付类型。该规范包含您的业务逻辑(>65(,为年龄驱动的需求提供上下文,并充当控制逻辑的中心位置。
您可以在此处阅读有关规格的更多信息:https://martinfowler.com/apsupp/spec.pdf