Closed albe closed 8 years ago
I'm not so sure about the conclusion to do side-effects in the ProcessManager any more. Either you don't register all your ProcessManagers during replay - then they may for example not emit Events themself (which is a trait I think is quite useful) - or you end up with the same problem as before.
My conclusion currently is one of two things:
SideEffect
, which works exactly like a ProcessManager
(reacting to a sequence of events) but is explicitly marked as having side effects and hence can automatically be avoided during replayWhat do you mean with side effect, can you provide an example and how you would suggest to solve it?
Prime example: Send registration confirmation email
You don't want that to happen every time you replay your aggregate.
As to suggestion, see above, but I'm fully open to other ideas
Thanks for the examples!
registration confirmation email [...] You don't want that to happen every time you replay your aggregate.
I wouldn't send those from the aggregate. And if so, only in the handling part like so:
class SomeAggregate
{
public function doSomething()
{
// validate
$this->recordThat(new SomethingHasHappened());
$this->someService->sideEffect();
}
This is not very error tolerant though, so I usually put those in a separate event listener:
class RegistrationEventListener
{
public function whenSomethingHasHappened(SomethingHasHappened $event)
{
$this->someService->sideEffect();
}
Yeah, Aggregate is actually a big design-error - it totally breaks single responsibility and also might blow up aggregate consistency (in case the side-effect errors out).
So the solution indeed is to have it in some EventListener (or rather ProcessManager as I described it in https://github.com/neos/Neos.Cqrs/issues/8#issuecomment-249173984), but as said above, for those you still need to somehow make it explicit that this EventListener should not be registered during replay.
So you end up with something like PureEventListener
and SideEffectEventListener
- or ReplayableEventListener
and UnreplayableEventListener
if you want to avoid the technical buzz words.
make it explicit that this EventListener should not be registered during replay
None of the EventListeners should ever be retriggered.
The only thing you can replay is a Projection
and that mechanism most probably won't require any bus: It fetches events with minVersion > projection.lastVersion and triggers the projector directly.
BTW: A ProcessManager
is only required if it keeps some internal state, probably not for the mentioned example of sending a notification (but I might miss sth)
I close this for now as we have everything in place (except documentation and good example) to deal with side-effects: It's just an EventListener
(which can be turned into a Saga/ProcessManager if it needs state)
Side-Effects are an integral part of nearly every application. Where to put them has therefore become a major concern in current software architectures that allow time-traveling:
http://jaysoo.ca/2016/01/03/managing-processes-in-redux-using-sagas/ https://github.com/reactjs/redux/issues/1528 http://danielwhittaker.me/2015/03/31/how-to-send-emails-the-right-way-in-a-cqrs-system/
I think this is something that we need to discuss and find a good solution for that works with replaying.