【问题标题】:How do I inject behaviour into an aggregate?如何将行为注入聚合?
【发布时间】:2017-10-01 05:56:19
【问题描述】:

我正在研究一个聚合,其中某些行为可以由应用程序中的多个角色执行。但在此之前发生相当复杂的验证。正是这种验证因角色而异。通常这意味着检查不同的配置设置以确定是否可以执行操作。

因此,举个例子:假设我有一个可以添加 OrderLines 的订单。如果我有角色员工,我可能被允许订购高达 100 欧元,-如果我有购买者角色,我可能被允许订购高达 1000 欧元,-.

您可以通过向 addOrderLine 方法提供用户实例来解决此问题,但这会将用户上下文泄漏到排序上下文中。下一个合乎逻辑的事情,这就是我一直在做的,是将验证逻辑注入方法调用。我正在调用这些方法策略并在应用程序服务中实例化正确的策略,因为我在那里有相关的用户信息:

<?php
class Order {
    public function addItem(OrderPolicy $policy, Item $item, int $amount) {
        if (!$policy->canPurchase($item->getPrice() * $amount))
            throw new LimitExceededException();

        /* add item here */
}

class OrderService {
    public function addItem(User $user, $orderId, $itemId, int $amount) {
        $order = $this->orderRepo->getForUser($user, $orderId);
        $item = $this->itemRepo->get($itemId);
        $policy = $this->getOrderPolicyFor($user);

        $order->addItem($policy, $item, $amount);
    }
}

class PurchaserOrderPolicy
{
    function canPurchase($amount) {
        return ($amount <= 1000);
    }
}

这看起来不错,但现在在我看来,我的排序上下文具有基于用户角色(策略类)的逻辑,它不应该知道。

DDD 是否提供任何其他处理此问题的方法?也许规范?这看起来不错吗?策略类属于哪里?

【问题讨论】:

  • 我建议将逻辑转移到服务中,不要让它传播到您的聚合中。只需在 OrderService 中进行检查,如果不允许用户添加商品,则会从服务中抛出异常。

标签: php domain-driven-design


【解决方案1】:

您似乎在这里涉及两个子域/系统:订购系统和购买政策系统。你需要把事情分开,你的直觉是正确的。这意味着关于最大订单价值的验证以相对于实际添加的项目的最终一致方式进行检查。您可以在之前(尝试防止无效订单)或之后(尝试从无效订单中恢复)执行此操作。

如果您在此之前执行此操作,则应用程序服务可以安排此操作并拒绝订单。

如果您在之后执行此操作,则可以将其作为一个进程实现,并拥有一个 ApplyPoliciesToOrdersSaga 来监听 ItemWasAddedToTheOrder 事件(如果您有事件驱动的架构)或作为计划任务/cron 作业运行并根据策略检查所有订单(在经典架构中)。

【讨论】:

  • 那么,在事先处理这个问题时,您会在聚合之外调用策略,那么在这种情况下,在应用服务中?我将它包含在聚合中,因为这似乎是它应该保护的不变量之一。你怎么看这个?
  • @NSSec 是的,我会从另一个系统调用查询服务,如果允许该项目,那么也调用本地聚合。策略模式(如果有的话)看起来不错,但正如我所说,你在另一个系统中使用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多