如何将行为注入聚合中?

时间:2017-10-01 05:56:19

标签: php domain-driven-design

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

所以,作为一个例子:假设我有一个可以添加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是否提供其他任何处理方法?也许规格?这看起来好吗?政策类属于哪里?

1 个答案:

答案 0 :(得分:2)

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

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

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