I know that the general concept behind event sourcing is that the state of the application should be able to be replayed from the event stream.
Sometimes, however, we need to get information for business rules from other parts of the system. i.e. An account has a user. A user has a blacklist status which is required to check if they can access/edit the account.
In the below example (purely for demonstration purposes), a user tries to subtract $10 from their account. If a user has been blacklisted, then we do not want to allow them to remove any funds from the account but we do want to record that they have tried to.
After the request is made, we could query the user model to see if the blacklist exists. If true then we can record it and throw the exception.
The user table/model is currently not event-sourced.
Now when we try to replay the event stream to re-build the projections with the state of the user is not being stored in events, it is no longer possible.
So assuming my current example does not work my questions are:
If we were to move the user into an event stored system (in a different aggregate but all events within the same event-stream) then would it be acceptable to use read models within business rules?
Is there any way we can mix event-sourced and CRUD into the same system when they may depend on each other for business rules.
public function subtractMoney(int $amount)
{
if ($this->accountOwnerIsBlacklisted()){
$this->recordThat(new UserActionBlocked());
throw CouldNotSubtractMoney::ownerBlocked();
}
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));
}
private function accountOwnerIsBlacklisted(): bool
{
return $this->accountRepositry()->ownerUser()->isBlackListed();
}