Laravel Event Sourcing(Spatie)-如何处理更复杂的业务规则

问题描述 投票:0回答:1

我目前已经开始使用事件处理套件spatie/laravel-event-sourcing进行事件外包领域。

[在遵循了基本的设置和指南以及阅读/观看了许多其他事件源指南之后,我已经有了基本的了解,但是正在努力理解应该如何实施更复杂的业务规则。

简单规则-银行结余示例

下面的例子很简单,很有意义。您从每个事件的余额中减去钱。如果您低于阈值,则在允许将其降低到更低之前会引发异常。

public function subtractMoney(int $amount)
{
    if (!$this->hasSufficientFundsToSubtractAmount($amount)) {
        $this->recordThat(new AccountLimitHit());

        if ($this->needsMoreMoney()) {
            $this->recordThat(new MoreMoneyNeeded());
        }

        $this->persist();

        throw CouldNotSubtractMoney::notEnoughFunds($amount);
    }

    $this->recordThat(new MoneySubtracted($amount));
}

protected function applyMoneySubtracted(MoneySubtracted $event)
{
    $this->balance -= $event->amount;

    $this->accountLimitHitInARow = 0;
}

更复杂-其他属性

在上面的示例中,我们只有一个属性(数量)。

在我的用例中,我的总根是产品库存。我希望聚合能够捕获不同存储位置之间的所有库存移动。即。

  • 将库存接收到一个位置
  • 库存从一个位置A移到位置B
  • 从位置A将库存分配到订单1
  • 从位置A选择订单1的库存
  • 库存被打包到订单1的装运包裹Z中

进行投影实际上非常简单。例如,我的库存投影仪显示每个位置的每种产品,以及接收,分配的库存量等。

public function onStockReceived(StockReceived $event, string $aggregateUuid)
{
    $inventory = Inventory::firstOrNew([
        'product_id' => $aggregateUuid,
        'location_id' => $event->locationId
    ]);

    $inventory->received += $event->amount;

    $inventory->save();
}

我与此有关的问题在尝试确定业务规则时又回到了聚合根目录中。感觉就像我必须在投影仪中复制代码才能检查事件中的数据。

在下面的示例中,我要确保提供的库存不超过收到的库存。即不要低于0。这开始让我感到非常复杂,并且已经在我的投影机中存储了重复的计数。

public function makeStockAvailable(int $amount, $locationId)
{
    if($this->hasInsufficientStockToMakeAvailable($amount, $locationId)){
        throw CouldNotMakeStockAvailable::insufficientStock($amount, $this->locations[$locationId]['received']);
    }

    $this->recordThat(new StockMadeAvailable($amount, $locationId));

    return $this;
}

public function applyStockMadeAvailable(StockMadeAvailable $event)
{
    $this->stockMadeAvailableInLocation($event->amount, $event->locationId);

    $this->availableStockTotal($event->amount);
}

private function stockMadeAvailableInLocation($amount, $locationId)
{
    $this->locations[$locationId]['received'] = $this->locations[$locationId]['received'] ?? 0;

    $this->locations[$locationId]['received'] -= $amount;

    $this->locations[$locationId]['available'] = $this->locations[$locationId]['available'] ?? 0;

    $this->locations[$locationId]['available'] += $amount;
}

private function availableStockTotal($amount)
{
    $this->received -= $amount;

    $this->available += $amount;
}

private function hasInsufficientStockToMakeAvailable($amount, $locationId): bool
{
    if(isset($this->locations[$locationId]['received'])){
        return $this->locations[$locationId]['received'] - $amount < 0;
    }

    return false;
}

我认为在我的聚合根目录中使用Eloquent(查看投影机)不可行,因为这将导致大量的数据库查询,并且不确定AR是否应依靠投影进行决策?

此外,由于已经批准了该活动,因此无法向我的投影仪添加任何业务规则。

我真的很喜欢spaties软件包,因为它使事件处理的基础变得容易,但是感觉到要使其与更复杂的解决方案一起使用时,已经有了很大的进步。

php laravel event-sourcing spatie
1个回答
0
投票

我认为这里的主要问题是您跳过了event-sourcing存储events中的一个重要事实。

在您的投影内部,好像您正在存储实体。投影基本上是Listeners对发生的事情有反应。

通常,您需要应用以下方案

  1. 从事件列表组成汇总根(如果还没有事件,则为空)
  2. 在您的AR中应用业务逻辑(这应该是业务不变性的真实来源)
  3. 记录类似StockMadeAvailable的事件
  4. 将事件存储在Event Store中->可能是MySQL
  5. 您可以使用投影来监听事件或外部事件并更新Read模型,它们基本上是通过提供高性能查询的方式开发的DB表

此外,Spatie还为Event Sourcing提供了基础结构,但是您自己的集合/实体/业务不变量的组成仍然取决于您的业务需求

© www.soinside.com 2019 - 2024. All rights reserved.