【发布时间】:2021-10-16 20:26:54
【问题描述】:
想象一下,我们的任务是实现一个 API 来检查是否可以将折扣计数应用于订单。 Order 域对象包含购物篮中的物品以及客户 ID:
class Order(
val items: List<Item>,
val customerId: CustomerId
)
我们还有一个域对象DiscountCode 表示要使用的折扣计数。
有几个验证规则可以检查给定的折扣计数是否可以应用于给定的订单:
- 折扣计数是否过期?
- 订单中有不能打折的商品吗?
- 此折扣代码是否已被其他人使用?
- (客户可以使用这个折扣码吗?)
对于规则 1-3,我们可以说它们显然是业务逻辑,根据 DDD 属于 DiscountCode 聚合:
class DiscountCode(
val id: DiscountCodeId,
val hasAlreadyBeenUsed: Boolean,
val startTime: LocalDateTime,
val endTime: LocalDateTime
) {
fun isApplicableToOrder(order: Order) {
return
startTime.isBefore(now) && endTime.isAfter(now) // rule 1
&& order.items.none(_.canNotBeDiscounted) // rule 2
&& !hasAlreadyBeenUsed // rule 3
}
}
我们可以轻松地从数据库中加载这个DiscountCode,然后调用上面的函数来检查它是否可以与给定的订单一起使用而不会产生任何副作用。
问题是如何处理规则 4:规则 4 不能仅使用 DiscountCode 类检查,除非我们将所有允许客户的列表嵌入到 DiscountCode 类中,如果有数千个,这是不可行的顾客。同样,我们不能将允许的折扣代码列表嵌入到 Customer 类中,因为可能有很多。在数据库中,我们可以添加一个包含有效客户 + 折扣代码元组的新表:
class DiscountCodeCustomerBinding(
val customerId: CustomerId,
val discountCodeId: DiscountCodeId
)
因此,为了检查规则 4,我们需要对该数据库表进行另一次查询。
遵循 DDD,规则 1-3 和规则 4 的业务逻辑应该放在哪里?我们不能对DiscountCode 类中的规则 4 进行数据库查询,因为它是一个副作用。我们可以将规则 1-4 移动到允许进行数据库查询的域服务中,但现在我们创建了一个贫血的域模型。将规则 1-3 放入 DiscountCode 类,将规则 4 放入单独的域服务中,将逻辑拆分为几个非常容易出错的地方。
【问题讨论】: