Many organizations are choosing to build more event-driven application architectures. This enables a subscriber or target services to automatically perform work in response to events triggered by publishers or source services. This event-driven pattern allows teams to operate more independently, releasing new features faster and making their applications more scalable.
In this blog post, I will write about how an event-driven architecture compares to an API-driven architecture and also what benefits as well as downsides you can expect from an event-driven architecture.
API-driven- and Event-driven architecture comparison
Most people working within technology know or have heard of API-driven architecture, to get a better understanding of how an Event-driven architecture works and what distinguishes the two, I will compare the two and highlight their different characteristics.
To clarify what an event-driven application is, you can think of it as a system that reacts to events from other things, such as within your application. With this approach, you will focus on how your system interacts with its surroundings as transmission of events. In essence, your application receives events that act as inputs and create events that act like outputs.
The below table briefly compares how the different architectures differ.
API-driven | Event-driven |
Synchronous | Asynchronous |
Request/Response | Subscribe to event notification |
Directed to a target | Happened in the past |
“Do this” | “This has been done” |
Many applications work together through an API-driven request/response architecture, so when you call your API, the API responds with the desired output. This is a synchronous architecture. When you call your API, you wait for the response, and you cannot move forward until that response comes back. If you would mimic this API-driven request/response pattern to a scenario in real life, it could be something similar to the below.
In an asynchronous event-driven architecture, the service surfaces the event and then moves forward. The trade-off you have to deal with here is that there is no direct response to pass back from the target service, besides confirming that the service received the event. This is however not a problem if you do not need that direct coupling between the request and response, meaning that you necessarily do not need to know more than that the event is received. Similar to the above, if you would mimic an event-driven communication pattern to a scenario in real life, it could be something similar to this.
Event-driven architecture and API-driven architecture also differ in how they store a given state. In most cases for an API-driven architecture, you only have one component that stores a piece of data and then other components ask for that data when needed. When comparing this to event-driven architecture, every component stores all the data it needs and listens to update events for that data. In API-driven architecture, the component that would store the data is also responsible for updating it. In an event-driven architecture, all the component has to do is to make sure that new events are raised on the updates. This makes event-driven architecture much more loosely coupled.
What is an event?
So to build event-driven architectures, you will need events of course. So what is an event? An event is something that already has happened. It could be that a VPC has been created or an object being put in an S3 bucket or a payment successfully processed. When an event has happened, you cannot change it, just as events that can happen in real life. For example, if there is an event raised when a payment is completed, there can be another event when you get a refund. Events can be triggered by anything, databases, compute services, object storage, etc.
Events in AWS are JSON objects that tell you what happened at a point in time in your application or infrastructure. In an event-driven application, each component raises an event when anything changes, and then other components can listen to this event and choose what to do with it.
Below is an example of what an event can look like.
{
"version": "0",
"id": "0f267645-245f-e8r6-95d4-65tv5f796c67",
"detail-type": "OrderCreated",
"source": "awesomeapp.orders",
"account": "123551231234",
"time": "2022-07-04T19:24:54Z",
"region": "eu-west-1",
"detail": {
"metadata": {
...
},
"data": {
"total_amount": "8.99",
"quantity": "1",
"customer_id": "0e092541-475d-c4f7-36d5-32gr5f733d52",
"orderId": "0f092540-635a-c4c7-36c5-32fe5f643d32",
"userId": "4e113dfa-r425-6e36-46e3-5627fb2e51ea"
}
}
}
In this OrderCreated
Event, we have some information our downstream services could subscribe to. For example, if you would have a packaging system you would probably be interested to know any of the provided properties in the data section. You can also choose to filter out parts of information in this event that might be interesting for other services that you develop.
➕ Benefits of an event-driven architecture
Decoupling and failing independently
Events are received by the event router to which your different services are subscribed, they are only aware of the event router and can operate independently. If one of your services fails, the rest will keep running.
Agility
This somewhat goes hand-in-hand with decoupling but as the event router receives the events and targets its subscribers, you can easily add a new subscriber without any of the existing subscribers being aware of it. This makes it easier to add new services, move them around and develop without impacting other services.
Responsiveness
Since your services will start working on their task as soon as possible and not wait for other services in your landscape, event-driven architecture can receive great response times.
Easier to audit
As your event router will be the central location in which you receive your events which later can be routed somewhere else, it's also easier to audit your application and define policies of what's allowed.
Understanding business needs
When building applications many organizations usually work together with other teams to gather requirements for what the application or feature is supposed to do. Developers then translate those requirements into code. This sounds easy but this is where things sometimes fail, the understanding of what's expected from the business stakeholders is not how developers understand it. When working with events, it is much easier for everyone in an organization to understand the logic. You define the events in an application and that becomes your product requirements, when this happens the application should do this. When that event occurs, anyone interested in that event can take action. Organizations can easily describe their business problems and represent them in the form of events.
➖ Drawbacks of an event-driven architecture
Difficulty in understanding what's going on
When designing loose dependencies between different services that use an event-driven architecture, you will need to observe the events and how the system behaves, investigating various flows that have been triggered as a result of events consumption and their processing. This can make it difficult to understand what's going on in the system by just reading the code.
Eventual consistency
Because your application now relies on eventual consistency, meaning that if no new updates are made to a given data item, eventually all accesses to that item will return the last updated value. Due to this, it does not commonly support ACID (atomicity, consistency, isolation, durability) transactions. When dealing with duplications or out-of-sequence events, your code is also prone to get more complicated.
No direct response of values
An event-driven application is asynchronous, which means that the consumer services do not wait for a response from other services before moving on with other work. This is a scalability and flexibility benefit of event-driven architectures but can also be a downside. When developing a synchronous type of architecture for your application, it is usually simpler to pass return values or results of a workflow to the consumer compared to the asynchronous type of architecture.
Debugging your services
Debugging event-driven applications or any microservices for that matter often requires a different approach than you’d take with a monolith. When your different services are passing events around, it gets a lot harder to reproduce the same state of your services when an error occurs. Your log files, metrics, and traces will most likely be located in different places which makes it difficult to debug across services. Proper tooling and correctly stamping out transaction IDs are key in troubleshooting your application.
Conclusion
In this post, we have touched upon how an event-driven architecture works and how it compares to an API-driven architecture. We have also explained its benefits and how event-driven scenarios happen in real life to better understand its capabilities. We have also outlined the benefits and the drawbacks of building event-driven applications.
So is event-driven architecture the silver-bullet way of building applications that you always wished for? No, but it is the best you can get for the right use-cases. It can be a powerful way of building your microservices as it helps to address some of the inherent challenges in building complex systems. But as with a lot of other things, this way of building does also have downsides and complications that might not just work for your architecture as we've mentioned in this post. You have to decide whether you can live with the drawbacks to using the advantages based on your project’s needs and requirements.
Elva is a serverless-first consulting company that can help you transform or begin your AWS journey for the future