Challenges of EDA
Adopting EDA patterns for your application brings along some challenges that must be overcome for the application to succeed.
Eventual consistency
Eventual consistency is a challenge for any distributed application. Changes in the application state may not be immediately available. Queries may produce stale results until the change has been fully recorded. An asynchronous application might have to deal with eventual consistency issues, but without a doubt, an event-driven application certainly will.
Dual writes
Not entirely a challenge of event-driven applications alone, dual write refers to any time you’re changing the application state in two or more places during an operation. For an event-driven application, this means we are making a change locally to a database, and then we’re publishing an event either about the event or the event itself. If the events we intend to publish never make it to the event broker, then our state changes cannot be shared, and post-operation operations will never happen.
For this challenge, we have a solution that will have us publish our events into the database alongside the rest of the changes to keep the state change atomic.
This allows a second record of to-be-published events to be created, and even adds additional resiliency on top of what we got from using an event broker between components, as illustrated in the following diagram:
Figure 1.14 – Outbox pattern
We will learn more about this challenge and solution when I introduce you to the Outbox pattern in Chapter 6, Asynchronous Connections.
Distributed and asynchronous workflows
Our third challenge involves performing complex workflows across components using events, making the workflow entirely asynchronous. When each component is coupled this way, we experience eventual consistency. Each component may not have the final state of the application when queried, but it will eventually.
This creates an issue for the UX and one for the collaboration of the components of the application involved with the operation. Each will need to be evaluated on its own to determine the correct solution for the problem.
UX
The asynchronous nature of the operation would obviously make it difficult to return a final result to the user, so the choice becomes how to handle this limitation. Solutions include but are not limited to fetching the result using polling on the client, delivering the result asynchronously using WebSockets, or creating the expectation the user should check later for the result.
Component collaboration
There are two patterns we can use to bring components together to manage workflows, as illustrated in the following diagram:
Figure 1.15 – Workflow choreography and orchestration
- Choreography: The components each individually know about the work they must do, and which step comes next
- Orchestration: The components know very little about their role and are called on to do their part by a centralized orchestrator
We will dive into the differences, some of the details to consider in choosing one over the other, and more in Chapter 8, Message Workflows.
Debuggability
Synchronous communication or P2P involves a caller and callee. This method of communication has the advantage of always knowing what was called and what made the call. We can include a request ID or some other unique ID (UID) that is passed on to each callee.
One of the disadvantages of EDA is being able to publish an event and not necessarily knowing if anything is consuming that event and if anything is done with it. This creates a challenge in tracing an operation across the application components.
We might see multiple operations unrelated to one another spring up from the same event. The process to trace back to the originating event or request becomes harder as a result. For an event-driven application, the solution is to expand on the solution used for P2P-only applications, and we will see crumbs of this solution throughout the book and discuss it in more detail in Chapter 12, Monitoring and Observability.
Testing the application using several forms of tests will be covered in Chapter 10, Testing.
Getting it right
It can be challenging for teams to think in terms of events and asynchronous interactions. Teams will need to look much more closely and know the application that they’re building better to see the small details that sometimes make up events. In Chapter 2, Supporting Patterns in Brief, we will look at some patterns that teams can use to break down the complexities of an application, and how to make managing and maintaining event-driven applications easier in the long run.
In Chapter 3, Design and Planning, we will cover tools that teams can use to break down an application into behaviors and the events associated with each one.
Big Ball of Mud with events
A Big Ball of Mud (BBoM) is an anti-pattern, where an application is haphazardly designed or planned. We can end up with one in our event-driven application just as easily with events as without and perhaps even more easily if we do not do a good job identifying behaviors and events.