Event-Driven Architectures (EDAs) vs Event Sourcing

Tom Oram
Cloudnative.ly
Published in
6 min readJul 19, 2023

--

Event Sourcing and Event-Driven Architecture often appear together; many blog articles mix the two and some books introduce them in the same section, but they are two different things. They can be used together, and it is often appealing to do so because both are centred around thinking in terms of events. However, both patterns are non-trivial, so it is essential that we clearly understand the differences so we can design the complicated systems that use them successfully.

Basic Application

Before introducing the concepts, let’s consider a simple scenario to extend with these patterns. A user interacts with a basic application, which stores and retrieves data from a database. Admittedly, most applications are significantly more complicated than this in the modern age, but this is a familiar scenario and the perfect starting point. We program the business logic in the application and use the database to store the current state of the entities in the system.

Application with a database

Event-Driven Architecture

With Event-Driven Architectures, we use events to communicate change between system parts. When an event occurs in some part of the system, we publish it so the other components can choose to react. This pattern can be used within a monolithic application using the observer design pattern; however, in modern systems, this is often distributed between in-dependent services using message queues.

Event-Driven Architectures have to embrace asynchronicity and eventual consistency.

Event-Driven Application

Event Sourcing

Event Sourcing is a persistence pattern. Instead of having a database that stores the current state of the entities, we have an immutable event log that stores the history of all the changes made. The event log is the single source of truth. The current state is obtained by replaying the events in the application.

The event log provides an audit trail and the ability to construct the state at any point in history.

For more details on Event Sourcing, see my previous article, What is Event Sourcing.

Event Sourced Application

Using Event Sourcing with Event-Driven Architectures

Event-Driven Architectures and Event Sourcing are two different things; the former is a communication pattern while the latter is a persistent state pattern. The only thing these two things have in common is events. However, the fact that they are both centred around events makes them both appealing to people building systems based on events.

However, there is an important question…

Are event-sourced events and event-driven events the same?

The default answer here should be “No”; here’s why:

Considering the pure concept of Event Sourcing, your event log is your database; the only service that reads your database is the event-sourced service. Its implementation, structure, and existence are purely an implementation of your service. On the other hand, the events published by a service are part of its public interface; consuming services are coupled to the events and their structure. When you expose the event-sourced events, you make them part of your public interface. In addition, you may only want to publish some of your events or some part of their content; you want to provide a limited subset. Conversely, you may also want to publish additional events that are not part of the state.

Event-Driven and Event Sourced Architecture (different events)

The non-default answer is “It depends”

Sometimes, it is too convenient to publish the event-sourced events to other services; you might find that when keeping them separate, you have to create two event datatypes for every new event. When this is the case, there is probably a tight relationship between the services publishing the events and those consuming them. They are likely local components of the same service, developed within the same team. If they are not, then this decision should be made very cautiously.

Command-Query Responsibility Segregation Architecture (CQRS)

One pattern that is bound to come up when talking about distributed and event-sourced systems is Command-Query Responsibility Segregation (CQRS). CQRS is a detailed pattern, although I’m skipping a lot of that detail in this example; I’m simply separating the write side (where commands are sent) of the model from the read side (where queries are made). It also does not need to be used with events (although they are an excellent fit). This pattern optimises the read model for high-performance reads at the cost of eventual consistency. It is excellent for systems with a high ratio of reads to writes. For example, a user may make a lot of requests while browsing products on an online store and then make just one purchase request at the end. CQRS is not a good fit if you have a 1:1 reading and writing ratio; it has probably only been added to mask slow performance on the write side by acting as a read cache.

Command-Query Responsibility Segregation (CQRS)

For this example, I have used an event-sourced write service and an event-driven read service. This whole thing could be one service in a larger system. A single team might be building it as a single service as one business component in the system. In this case, the read database could simply be a viewable projection of the event log; the events that drive the read service are the events in the event log.

However, this model could also be distributed further. The writing service is one application built by one team, and the read service is a consumer service created by another team. In this case, it makes sense for the events to be different, curated ones.

Writes might not require state

One of the key points in the previous example is that the write service fetches the state from the event log. This is very important in a system where state changes depend on the current state. However, you may have a system where the commands generate events without referencing the current state. In these cases, the write service never reads back from the event log. The write service generates and publishes the event, and then the read service consumes it. From that aspect, the read service is event-driven. However, the event log is still the single source of truth, so we could also argue that it is event-sourced.

This pattern is more straightforward with a tool like Kafka as the event queue, because it can also persist the streams and behave as the log.

CQRS application without writer state

Summary

Event-Driven Architectures are a communication pattern, whereas Event Sourcing is a persistent state pattern. When looked at independently, they are two very different things, but combining them in different ways can blur the distinctions. Also, both patterns bring advantages, but at the cost of being more complicated. Finally, the locality of the services that consume your events affects which events you should expose.

My final suggestions are:

  • Make sure you understand each pattern in isolation before combining them.
  • Consider carefully if they are suitable patterns for your problem.
  • Only expose your event-sourced events if the consuming service is very local. Otherwise, publish specific events for external consumption.

--

--

Passionate about all aspects of software. Engineer at Armakuni.