Php Laravel事件源(Spatie)-在业务规则中使用预测

Php Laravel事件源(Spatie)-在业务规则中使用预测,php,laravel,event-sourcing,spatie,Php,Laravel,Event Sourcing,Spatie,我知道事件源背后的一般概念是应用程序的状态应该能够从事件流中重放 然而,有时我们需要从系统的其他部分获取业务规则的信息。i、 e.一个帐户有一个用户。用户具有黑名单状态,需要检查其是否可以访问/编辑帐户 在下面的示例中(纯粹出于演示目的),用户试图从其帐户中减去10美元。如果一个用户被列入黑名单,那么我们不希望允许他们从帐户中删除任何资金,但我们希望记录他们试图删除的信息 在发出请求之后,我们可以查询用户模型以查看黑名单是否存在。如果为true,那么我们可以记录它并抛出异常 用户表/模型当前不是

我知道事件源背后的一般概念是应用程序的状态应该能够从事件流中重放

然而,有时我们需要从系统的其他部分获取业务规则的信息。i、 e.一个帐户有一个用户。用户具有黑名单状态,需要检查其是否可以访问/编辑帐户

在下面的示例中(纯粹出于演示目的),用户试图从其帐户中减去10美元。如果一个用户被列入黑名单,那么我们不希望允许他们从帐户中删除任何资金,但我们希望记录他们试图删除的信息

在发出请求之后,我们可以查询用户模型以查看黑名单是否存在。如果为true,那么我们可以记录它并抛出异常

用户表/模型当前不是事件源

现在,当我们尝试重播事件流以重新构建投影时,用户的状态没有存储在事件中,这就不可能了

因此,假设我当前的示例不起作用,我的问题是:

  • 如果我们将用户移动到事件存储系统中(在不同的聚合中,但所有事件都在同一事件流中),那么在业务规则中使用读取模型是否可以接受

  • 当事件源代码和CRUD在业务规则上相互依赖时,我们是否可以将它们混合到同一个系统中


  • 因为您基本上是在使用DDD(没有提及它),所以答案可能在于那里的定义。在DDD中,您应该定义每个聚合根的边界。每个聚合根不应存储对其他聚合根的任何依赖关系(Spatiale包甚至不支持它)。它应该只由事件组成,然后成为真相的唯一来源

    根据您的示例,似乎阻止用户不是由于其帐户上的负面事件,而是由于与其用户(帐户所有者)相关的事件。这里的关键字似乎是“所有者”。如果您想存储用户试图取款的操作发生的事实,那么您仍然可以应用该事件,但在本例中,原因将来自另一个聚合“用户”。用户本身是否是事件来源并不重要,但用户实体有方法检查用户是否被阻止,因此您的系统中的业务规则是不允许他从帐户提款。 如果您不能同时对这两个进行建模,那么我建议您设计一个能够处理此命令的域服务。如果可以的话,尽量将它们作为模型的一部分,以避免使域模型变得贫乏

    <?php 
    class AccountWithdrawalService
    {
        public function __construct(UserRepository $userRepository)
        {
            $this->userRepository = $userRepository;
        }
    
        public function withdraw($userId, $accountId, $amount)
        {
            $user = $this->userRepository->find($userId);
    
            // You might inject AccountAggregateRoot too.
            $account = AccountAggregateRoot::retrieve($accountId);
    
            if(!$user->isBlackListed())
            {
                $account->subtractMoney($amount);
            }
            else
            {
                // Here we record the unhappy road :-( 
                $account->moneySubtractionBlocked($amount);
            }
    
            $account->persist();
        }
    }
    

    我喜欢这个回答。服务类还可以处理大量业务逻辑,而不必膨胀聚合根。
    
    <?php 
    class AccountWithdrawalService
    {
        public function __construct(UserRepository $userRepository)
        {
            $this->userRepository = $userRepository;
        }
    
        public function withdraw($userId, $accountId, $amount)
        {
            $user = $this->userRepository->find($userId);
    
            // You might inject AccountAggregateRoot too.
            $account = AccountAggregateRoot::retrieve($accountId);
    
            if(!$user->isBlackListed())
            {
                $account->subtractMoney($amount);
            }
            else
            {
                // Here we record the unhappy road :-( 
                $account->moneySubtractionBlocked($amount);
            }
    
            $account->persist();
        }
    }