Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Event sourcing for synchronous concerns

I struggling to understand how I can design a backend that is event driven using event sourcing that can support synchronous requests. From what I understand, to take advantage of event sourcing, you must develop the system to react to events so that you can replay them if necessary to recreate your state. To do so, this means that we are trying to decouple event triggers and event handlers.

If we assume a case in which a client is sending a request to update some data, how can we accomodate this synchronous request/response model while using event driven systems? Would you say the below steps are the correct way to handle requests in an event driven manner:

  1. Receive network request at API gateway, or some service at edge of network, and emit an event that represents this request. The API gateway at this point will hang and wait.

  2. The emitted event is captured by the event store and is logged

  3. A service with the business logic to handle the update captures the event as it is subscribed to the event store. It produces a success event if it was able to handle the update or an error event if it was unable to do so.

  4. the API gateway is receives either one of success or error events it was waiting for and finally sends back the response to the browser.

I think the above does a great deal to separate concerns but I'm skeptic if this is the way of enabling event sourcing with a system that accepts requests from an external client.

like image 910
alaboudi Avatar asked Oct 30 '19 11:10

alaboudi


1 Answers

You’re mixing up several terms and ideas, which are important to keep separate. Once you’ve separated those in your head, all should become clear.

Event Sourcing and Event Driven Architecture are two different ideas. Event Sourcing means you keep all changes to an entity in an Event Store and to get the current state of that entity you add up all the events. This concept relates to storage of state, not processing of requests. An Event Driven Architecture is what I think you’re referring to in this question. The need to keep these ideas separate will become clear in a moment.

In an Event Driven Architecture there are two ideas you need to keep separate. There’s the request coming in from a UI or external system to make some change to the data; this is a message or request. The message handler (usually called a command in CQRS) processes the request and either rejects it as invalid or makes appropriate changes to the entities in the system. The changes to those entities are then store as Events in the Event Store. The events in the Event Store should only contain the data that changed, not all the properties of the entity. If more than one entity is changed by the command, more than one event will be written to the event store.

The system can have listeners (or subscribers) to the event store and other business logic can be launched in response to a change in an entity. These changes are run async in an eventually consistent manner.

With all this in mind, we can talk about how an Event Driven Architecture handles both synchronous and async events. Keep in mind that EDA is designed for asynchronous processing and eventual consistency so making it synchronous is a bit of extra work.

Using your 4 steps above we get this

Step 1: No changes to the logic of your description. The gateway receives the request (not an event) and waits.

Step 2: Some developers like to store the incoming requests for pattern analysis and such, but it is not necessary to successfully process the request.

In an eventually consistent architecture the business logic would do some basic validation of the incoming request and if it looks good send back a confirmation that the operation was successful. The request would be put on a queue and handled when convenient. If errors are encountered, the user is notified later.

But since your system is synchronous, the API (what you called the 'API Gateway') would directly call the service responsible for handling the business logic – the command.

Step 3: The request is handled by the command, validating the request and making any necessary changes to entities. Events are created for all entity changes (one event per entity).

Step 4: The command returns a success or failure value synchronously to the API, which returns it to the caller. Note that this should be an async/await call, not a blocking call from the API. To the API and the user it still looks like a synchronous process.

like image 129
Brad Irby Avatar answered Oct 22 '22 15:10

Brad Irby