Event-Driven Architecture
An event-driven architecture uses events to trigger and communicate between decoupled services and is common in modern applications built with microservices. An event is a change in state, or an update, like an item being placed in a shopping cart on an e-commerce website. Events can either carry the state (the item purchased, its price, and a delivery address) or events can be identifiers (a notification that an order was shipped).
Event-driven architecture have three key components: event producers, event routers, and event consumers. A producer publishes an event to the router, which filters and pushes the events to consumers. Producer services and consumer services are decoupled, which allows them to be scaled, updated, and deployed independently.
Implementing Asynchronous Event-Based Collaboration
We’ve talked for a bit about some technologoies that can help us implement request/ response patterns. What about event-based, asynchronous communication?
Technology Choices
There are two main parts we need to consider here: a way for our microservices to emit events, and a way for our consumers to find out those events have happened. Traditionally, message brokers like RabbitMQ try to handle both problems. Producers use an API to publish an event to the broker. The broker handles subscriptions, allowing consumers to be informed when an event arrives. These brokers can even handle the state of consumers, for example by helping keep track of what messages they have seen before.
These systems are normally designed to be scalable and resilient, but that doesn’t come for free. It can add complexity to the development process, because it is another system you may need to run to develop and test your services. Additional machines and expertise may also be required to keep this infrastructure up and running. But once it does, it can be an incredibly effective way to implement loosely coupled, event-driven architectures. In general, I’m a fan. Do be wary, though, about the world of middleware, of which the message broker is just a small part. Queues in and of themselves are perfectly sensible, useful things.
However, vendors tend to want to package lots of software with them, which can lead to more and more smarts being pushed into the middleware, as evidenced by things like the Enterprise Service Bus. Make sure you know what you’re getting: keep your middleware dumb, and keep the smarts in the endpoints. Another approach is to try to use HTTP as a way of propagating events. ATOM is a REST-compliant specification that defines semantics (among other things) for publishing feeds of resources. Many client libraries exist that allow us to create and consume these feeds. So our customer service could just publish an event to such a feed when our customer service changes.
Our consumers just poll the feed, looking for changes. On one hand, the fact that we can reuse the existing ATOM specification and any associated libraries is useful, and we know that HTTP handles scale very well. However, HTTP is not good at low latency (where some message brokers excel), and we still need to deal with the fact that the consumers need to keep track of what messages they have seen and manage their own polling schedule. I have seen people spend an age implementing more and more of the behaviors that you get out of the box with an appropriate message broker to make ATOM work for some use cases.
For example, the Competing Consumer pattern describes a method whereby you bring up multiple worker instances to compete for messages, which works well for scaling up the number of workers to handle a list of independent jobs. However, we want to avoid the case where two or more workers see the same message, as we’ll end up doing the same task more than we need to. With a message broker, a standard queue will handle this. With ATOM, we now need to manage our own shared state among all the workers to try to reduce the chances of reproducing effort.
If you already have a good, resilient message broker available to you, consider using it to handle publishing and subscribing to events. But if you don’t already have one, give ATOM a look, but be aware of the sunk-cost fallacy. If you find yourself wanting more and more of the support that a message broker gives you, at a certain point you might want to change your approach. In terms of what we actually send over these asynchronous protocols, the same considerations apply as with synchronous communication. If you are currently happy with encoding requests and responses using JSON, stick with it.
Benefits of an event-driven architecture
Scale and fail independently
By decoupling your services, they are only aware of the event router, not each other. This means that your services are interoperable, but if one service has a failure, the rest will keep running. The event router acts as an elastic buffer that will accommodate surges in workloads.
Develop with agility
You no longer need to write custom code to poll, filter, and route events; the event router will automatically filter and push events to consumers. The router also removes the need for heavy coordination between producer and consumer services, speeding up your development process.
Audit with ease
An event router acts as a centralized location to audit your application and define policies. These policies can restrict who can publish and subscribe to a router and control which users and resources have permission to access your data. You can also encrypt your events both in transit and at rest.
Cut costs
Event-driven architectures are push-based, so everything happens on-demand as the event presents itself in the router. This way, you’re not paying for continuous polling to check for an event. This means less network bandwidth consumption, less CPU utilization, less idle fleet capacity, and less SSL/TLS handshakes.
When to use this architecture
Cross-account, cross-region data replication
You can use an event-driven architecture to coordinate systems between teams operating in and deploying across different regions and accounts. By using an event router to transfer data between systems, you can develop, scale, and deploy services independently from other teams.
Resource state monitoring and alerting
Rather than continuously checking on your resources, you can use an event-driven architecture to monitor and receive alerts on any anomalies, changes, and updates. These resources can include storage buckets, database tables, serverless functions, compute nodes, and more.
Fanout and parallel processing
If you have a lot of systems that need to operate in response to an event, you can use an event-driven architecture to fanout the event without having to write custom code to push to each consumer. The router will push the event to the systems, each of which can process the event in parallel with a different purpose.
Integration of heterogeneous systems
If you have systems running on different stacks, you can use an event-driven architecture to share information between them without coupling. The event router establishes indirection and interoperability among the systems, so they can exchange messages and data while remaining agnostic.
Should you use an event-driven architecture?
Event-driven architectures are ideal for improving agility and moving quickly. They’re commonly found in modern applications that use microservices, or any application that has decoupled components. When adopting an event-driven architecture, you may need to rethink the way you view your application design. To set yourself up for success, consider the following:
• The durability of your event source. Your event source should be reliable and guarantee delivery if you need to process every single event.
• Your performance control requirements. Your application should be able to handle the asynchronous nature of event routers.
• Your event flow tracking. The indirection introduced by an event-driven architecture allows for dynamic tracking via monitoring services, but not static tracking via code analysis.
• The data in your event source. If you need to rebuild state, your event source should be deduplicated and ordered.
Common Event-Driven Architecture Concepts
Let’s dive into the nitty-gritty of event-driven architecture! Imagine it like a cool club where different systems mingle and chat through events. To truly groove in this club, you gotta get these eight key concepts:
Event Broker: Think of this as the bouncer of our club. It’s like a middleman that ensures events from one system reach all the others who are interested in them. So, if System A throws an event, the broker makes sure System B, C, and the whole gang hear about it.
Event Portal: Picture this as our event club’s HQ. It’s where we plan, discuss, and manage all our events. Architects use it to design stuff, developers use it to find events they need, and data scientists swing by to catch up on the latest data gossip.
Topics: These are like hashtags for events. They describe what’s happening in an event. So, if an event is about “New User Sign-up,” that’s its topic. It helps systems know what events to listen for and which ones to ignore.
Event Mesh: Now, think of this as the network of our club. It’s what connects all our event brokers together, making sure messages flow smoothly between systems, no matter where they are in the world.
Deferred Execution: This one’s like sending out party invites and not worrying about immediate RSVPs. When an event is sent, systems don’t have to act on it right away. They can chill and process it later when they’re ready.
Eventual Consistency: It’s like saying, “Hey, eventually, we’ll all be on the same page.” Since systems process events at their own pace, we can’t expect them to be in sync all the time. But eventually, they’ll catch up.
Choreography: Picture a dance floor where each system is busting its own moves. Instead of having a master planner, systems just react to events as they come, creating a synchronized dance of actions.
CQRS (Command Query Responsibility Segregation): This one’s about splitting tasks. It’s like having one team handle the party planning (commands) while another team manages guest inquiries (queries). This way, each team can focus on what they do best.
So, in a nutshell, event-driven architecture is all about letting systems communicate seamlessly through events, with each system doing its own thing and yet, somehow, everything falls into place. Cool, right?