我目前已经开始使用事件处理套件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;
}
在上面的示例中,我们只有一个属性(数量)。
在我的用例中,我的总根是产品库存。我希望聚合能够捕获不同存储位置之间的所有库存移动。即。
进行投影实际上非常简单。例如,我的库存投影仪显示每个位置的每种产品,以及接收,分配的库存量等。
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软件包,因为它使事件处理的基础变得容易,但是感觉到要使其与更复杂的解决方案一起使用时,已经有了很大的进步。
我认为这里的主要问题是您跳过了event-sourcing
存储events
中的一个重要事实。
在您的投影内部,好像您正在存储实体。投影基本上是Listeners
对发生的事情有反应。
通常,您需要应用以下方案
StockMadeAvailable
的事件Event Store
中->可能是MySQL
此外,Spatie还为Event Sourcing
提供了基础结构,但是您自己的集合/实体/业务不变量的组成仍然取决于您的业务需求