ings0c
ings0c

Reputation: 170

Loading events from inside event handlers?

We have an event sourced system using GetEventStore where the command-side and denormalisers are running in two separate processes.

I have an event handler which sends emails as the result of a user saving an application (an ApplicationSaved event), and I need to change this so that the email is sent only once for a given application.

I can see a few ways of doing this but I'm not really sure which is the right way to proceed.

1) I could look in the read store to see if theres a matching application however there's no guarantee that the data will be there when my email handler is processing the event.

2) I could attach something to my ApplicationSaved event, maybe Revision which gets incremented on each subsequent save. I then only send the email if Revision is 1.

3) In my event handler I could load in the events from my event store for the matching customer using a repository, and kind of build up an aggregate separate from the one in my domain. It could contain a list of applications with which I can use to make my decision.

My thoughts:

1) This seems a no-go as the data may or may not be in the read store

2) If the data can be derived from a stream of events then it doesn't need to be on the event itself.

3) I'm leaning towards this, but there's meant to be a clear separation between read and write sides which this feels like it violates. Is this permitted?

Upvotes: 1

Views: 142

Answers (1)

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57214

I can see a few ways of doing this but I'm not really sure which is the right way to proceed.

There's no perfect answer - in most cases, externally observable side effects are independent of your book of record; you're always likely to have some failure mode where an email is sent but the system doesn't know, or where the system records that an email was sent but there was actually a failure.

For a pretty good answer: you're normally going to start with a facility that sends and email and reports as an event that the email was sent successfully, or not. That's fundamentally an event stream - your model doesn't get to veto whether or not the email was sent.

With that piece in place, you effectively have a query to run, which asks "what emails do I need to send now?" You fold the ApplicationSaved events with the EmailSent events, compute from that what new work needs to be done.

Rinat Abdullin, writing Evolving Business Processes a la Lokad, suggested using a human operator to drive the process. Imagine building a screen, that shows what emails need to be sent, and then having buttons where the human says to actually do "it", and the work of sending an email happens when the human clicks the button.

What the human is looking at is a view, or projection, which is to say a read model of the state of the system computed from the recorded events. The button click sends a message to the "write model" (the button clicked event tells the system to try to send the email and write down the outcome).

When all of the information you need to act is included in the representation of the event you are reacting to, it is normal to think in terms of "pushing" data to the subscribers. But when the subscriber needs information about prior state, a "pull" based approach is often easier to reason about. The delivered event signals the project to wake up (reducing latency).

Greg Young covers push vs pull in some detail in his Polyglot Data talk.

Upvotes: 2

Related Questions