【发布时间】:2021-09-20 00:06:29
【问题描述】:
我目前已开始使用 spaties 的包 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 包,因为它使事件溯源的基础知识变得简单,但感觉有很大的飞跃使它可以用于更复杂的解决方案。
要解决的示例业务逻辑
public function moveAvailableStock(int $amount, $locationIdFrom, $locationIdTo)
{
if(($available = $this->locations[$locationIdFrom]['available'] ?? 0) < $amount){
throw CouldNotMoveStock::insufficientStock($amount, $available, $locationIdFrom);
}
$this->recordThat(new StockMoved($amount, $locationIdFrom, $locationIdTo));
return $this;
}
当我重播事件时,我需要通过其状态(已接收、可用、已分配)以及它们的存储位置来存储每个库存移动。我目前正在使用存储到私有变量 ($locations) 的数组来执行此操作,它可以工作,但感觉就像:
a) 它不会扩展。根据使用的位置数量,该数组可能会增长到数千个元素。
b) 它可能会很快变得复杂。这只是众多规则之一。
【问题讨论】:
标签: php laravel event-sourcing