Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Side Effects: Where to put and how to handle them #24

Closed
albe opened this issue Sep 23, 2016 · 7 comments
Closed

Side Effects: Where to put and how to handle them #24

albe opened this issue Sep 23, 2016 · 7 comments

Comments

@albe
Copy link
Member

albe commented Sep 23, 2016

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/
reduxjs/redux#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.

@albe
Copy link
Member Author

albe commented Sep 23, 2016

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:

  • Create an own BC for your side effects which itself keeps track to avoid duplicate actions (ie. keep track of handled command/event identifiers)
  • create an own abstraction for 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 replay

@bwaidelich
Copy link
Member

What do you mean with side effect, can you provide an example and how you would suggest to solve it?

@albe
Copy link
Member Author

albe commented Sep 26, 2016

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

@bwaidelich
Copy link
Member

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();
  }

@albe
Copy link
Member Author

albe commented Sep 28, 2016

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 #8 (comment)), 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.

@bwaidelich
Copy link
Member

bwaidelich commented Sep 28, 2016

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)

@bwaidelich
Copy link
Member

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants