Eventual Consistency is a UX Nightmare

Eventual Consistency is the term most people refer to when they are reading from a different data source from where they perform a write. Usually leads to a bad user experience where a user performs some action but then doesn’t see their change reflected in the UI immediately. There can be many reasons for this. It could be from using a read replica that is eventually consistent. If you’re using Event Sourcing with Projections as your Read Model, which is built asynchronously. Or if you’re processing commands asynchronously via a message queue.

YouTube

Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.

Replication Lag

A very common situation where eventual consistency occurs is with replication lag. If you’re using a database with read replicas that are eventually consistent and you perform queries against the read replica rather than the primary database.

You have a client that makes a request to your API to perform some type of command that is going to make a state change.

Eventual Consistency is a UX Nightmare

After the command request has been completed, the client then immediately makes a query request to get data that directly relates to the state change from the command.

Eventual Consistency is a UX Nightmare

The issue is because the read replica is eventually consistent, it hasn’t yet been replicated from the primary. This means that the query performed by the client is getting stale data.

Eventual Consistency is a UX Nightmare

After the query was made and returned stale data does replication occur and the read replica is now up-to-date. If the client makes another request it will get consistent data.

What’s more troublesome is this is a race condition as sometimes replication will happen quicker than the client query, this is entirely dependent on how quickly the client makes a request and how quickly replication is occurring.

Event Sourcing & Projections

One of the most common questions I get related to CQRS & Event Sourcing is eventual consistency. This occurs because you will often create projections (read models) that are used for queries. Since projections are built asynchronously, there is a lag between writing to your event stream in your event store and the projection being updated.

In a similar way as above, the client sends a command that makes a state change which results in a new event being appended to your event stream.

The client then immediately calls a query to retrieve data that is related to that state change.

Eventual Consistency is a UX Nightmare

But because our projection (event handlers) haven’t yet processed the event to update our read model (query database), we return the client stale data.

And just like with replication lag, we have a race condition where the projections may run after that query was made.

Async Processing

While not eventual consistency, processing requests/commands asynchronously off a queue can have a similar type of effect in a bad user experience.

The client sends a command but instead of it being handled in-process (blocking), rather the command is put on a queue.

The client isn’t possibly fully aware that the command wasn’t immediately processed. The client then makes a query which is returning what they believe to be stale data.

However this isn’t stale data, it’s just that the command has yet to be processed because of a race condition.

Finally, the message is pulled off the queue and processed and the database is updated.

While not eventual consistency, a similar UX issue as replication lag and event sourcing projections.

Solutions

In the majority of these cases, this is only a problem for a single user. This is because a user is trying to perform a write (command) and then subsequently perform a read (query) when either the data that they specifically changed hasn’t yet been either processed or made its way to the read database.

Basically, you’re trying to read your write but not using a fully consistent way.

Server Wait

One solution is to have the server wait (block) the client until the read database has been updated. This means when the client sends a command, the server won’t return back to the client until the data has been replicated (or projection updated, etc). This means that once the command completes and the client makes a query, they will not get stale data.

Client Polling

Instead of having the server wait, you instead can have the client wait. Once the client performs its write (command), it then performs the query but understands what the last read (version) was. If the version has not been updated, that means that the replication (or projection) has not been updated and it’s still stale data. The client will wait and then query again until it gets the updated data.

Push to Client

Another option is to push to the client once the replication (or projection) has occurred. This could be accomplished with something like WebSockets where you establish a connection from the client to server and then have the server push to the client to notify them the replication (or projection) has occurred. At this point, the client can then perform a query to get the latest data.

Read from Primary after Write

Finally, the last option is to read from the primary after you perform a write. Again this is only a situation for an individual user who performed the write (command).

What this means is once the client sends a command, the following query will be directed towards the primary, not the read replica (or query database).

There are different methods to do this, one of which is simply having a time window to direct all queries to the primary for the client that made a state change. For example, after a command, for a time window of 5 seconds, all queries are direct to the primary for the client that sent the initial command.

Eventual Consistency

In many situations, eventual consistency isn’t a problem. It’s often a UX problem because developers think they are in a fully consistent situation and they can read their own write. However, when this is not the case, you cannot have your UI perform like it will be consistent. If you do, your users will get confused when they hit a race condition where the query is performed before your read database is updated and the user sees stale data.

Source Code

Developer-level members of my YouTube channel or Patreon get access to the full source for any working demo application that I post on my blog or YouTube. Check out the YouTube Membership or Patreon for more info.

Follow @CodeOpinion on Twitter

Software Architecture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design

CQRS & Event Sourcing Code Walk-Through

Want to see an example of how CQRS & Event Sourcing work together? Here’s a code walk-through that illustrates sending commands to your domain that stores Events in an Event Store. The Events are then published to Consumers that updated Projections (read models) that are then consumed by Queries. This is the stereotypical set of patterns used when using CQRS and Event Sourcing together.

YouTube

Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.

CQRS & Event Sourcing

Because CQRS and Event Sourcing are so often talked about or illustrated together, you’ll end up seeing a diagram like the one below.

This is the stereotypical diagram to illustrate both concepts together. Unfortunately there often isn’t a distinction between what portion is CQRS and what portion is Event Sourcing. And as you’ll see later in this post, there’s another concept involved as well in this diagram as well.

CQRS

Command Query Responsibility Segregation (CQRS) is simply the separation of Writes (Commands) and Reads (Queries). In the diagram above, that’s illustrated by the horizontal paths on the top and bottom. I’ve talked about the simplicity of CQRS and the 3 Most Common Misconceptions.

Event Sourcing

Event Sourcing is about how you record state. It’s a different approach to persistence since most applications are built to only record the current state. Event sourcing is about using a series of events (facts) that represent all the state transitions that get you to the current state. If you want more of the basics, check out my post Event Sourcing Example & Explained in plain English.

Simplest Possible Thing

I’m going to use Greg Young’s Simplest Possible Thing sample that illustrates both CQRS, Event Sourcing, and Projections. This sample is rather old so I’ve upgraded it to .NET 6 and Razor pages.

The sample app is just showing an Inventory Item and has various commands that mutate state, queries that return current state, and event sourcing is used as a way to persist state.

Commands & Events

Commands are handled by Command Handlers. In this example, they are simply using a repository to get out the InventoryItem which is a domain object, and then invoke the proper method on the InventoryItem. The Repository.Save() will persist all the events (generated though ApplyChange() on the InventoryItem you will see below) to an in-memory event stream (collection).

The Inventory Item domain objects contain all the behavior for doing any state transitions.

The AggregateRoot base class contains the ApplyChange method, which stores the event being applied. It also calls the appropriate Apply() method on the InventoryItem. You will notice there are Apply() methods for only some of the events. This is because our InventoryItem only cares about maintaining its internal state (projection) that is required to perform logic.

What all this code is illustrating is the Command side of CQRS as well as Event Sourcing. Commands are explicit actions that we handle to perform some type of state change. For state changes, we’re using explicit events to capture what actually occurred from the result of a command.

CQRS & Event Sourcing Code Walk-Through

Projections

Before I illustrate the Query side of CQRS, first we’re going to build a Projection that acts as a read model representing the current state of the Events.

When events are saved by the Repository, it then dispatches the events to consumers. In this example, it is done in-memory however if you were using an actual Event Store, you’d likely be using a subscription model to have your projections run in isolation asynchronously.

Consumers handle the events to update the read model (projection). In this example, there are two different consumers for updating two different projections. One projection is for showing a list of Inventory Items, the other is for showing an individual Inventory Item.

This is just using a FakeDatabase that is really just an in-memory collection & dictionary.

CQRS & Event Sourcing Code Walk-Through

Query

Now that we have projections (read models) we can look at how the Query side of CQRS uses the projections.

The razor page is using the ReadModelFacade (and underlying FakeDatabase) that has the projection for our InventoryItem details.

CQRS & Event Sourcing Code Walk-Through

CQRS & Event Sourcing

Hopefully, this illustrated the differences between CQRS, Event Sourcing, and how they are used together while also using Projections. While this diagram is often used to describe CQRS, realize there are multiple aspects that are at play and not just CQRS.

Source Code

Developer-level members of my YouTube channel or Patreon get access to the full source for any working demo application that I post on my blog or YouTube. Check out the YouTube Membership or Patreon for more info.

Related Links

Follow @CodeOpinion on Twitter

Software Architecture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design

Should you use the Repository Pattern? With CQRS, Yes and No!

The repository pattern is polarizing. Some developers swear you should always use it to abstract data access logic while others think it’s unnecessary if you’re using an ORM. So should you use it? My answer is Yes and No! If you’re applying CQRS and Vertical Slice Architecture you’ll likely want a repository to build up Aggregates. However, for a Query, you may want to just get the data you need rather than an entire aggregate (or collection of aggregates) to build a view model.

YouTube

Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.

Repository

As with many terms and concepts in the software industry, a repository can mean different things depending on your definition. I’m using the definition from Martin Fowler’s P of EAA Catalog definition.

Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.

https://martinfowler.com/eaaCatalog/repository.html

The key to part for me of that definition is domain objects. Not data models, but domain objects.

CQRS & Vertical Slices

Often I’m using CQRS to separate pathways between writes (commands) and reads (queries) to my database. This allows the definition of two distinct paths that each can decide how they interact with the database, their dependencies, etc. This isn’t top-level architecture but just a decision you can make in various parts of your system.

Should you use the Repository Pattern? With CQRS, Yes and No!

As mentioned earlier, CQRS is a concept that is often confused. Check out my post CQRS Myths: 3 Most Common Misconceptions that should clear up any confusion.

What ultimately happens when you start focusing individually on a command or a query leads to organizing your code around features. A feature can be an individual command or query, or a collection of a few.

A Vertical Slice is a concept of taking everything related to a feature and organizing it together. As mentioned, this becomes a natural fit with CQRS. Ultimately a feature is a single use-case or a defined set of functionality within your system.

Should you use the Repository Pattern? With CQRS, Yes and No!

Vertical Slices are focused on features, not technical concerns. No longer are you organizing and writing code in a layered approach. The layers and technical separation are defined per feature.

This means you can define how each command or query handles various concerns, for example, data access.

If you go back to the definition of the Repository Pattern, it’s for accessing domain objects. Domain Objects that are grouped together are defined as an Aggregate. To interact with the Aggregate, all operations are handled by a primary domain object which is the Aggregate Root.

The common example often used is a Sales Order and all the Line Items. The Sales Order and Line Items are domain objects that form an Aggregate. The Sales Order is the Aggregate Root. All operations are done through the Sales Order and no access is done directly to any Line Items. Check out my post on Aggregate Design: Using Invariants as a Guide for more on how to define and design an aggregate.

This means that I’m only concerned with an aggregate for making state changes. In other words, an Aggregate is required for Commands, not Queries.

This means that we can define to use an Aggregate for any Commands, and simply use a Data Model for any Queries. We do not need an Aggregate for queries because our Aggregate is responsible for state changes.

Also, most of the time when creating a Query, you want data to be shaped a certain way. This doesn’t necessarily require everything within an Aggregate. Because of this, you’re often way over fetching data to build the Aggregate when you only need a subset of the data for the Query.

To illustrate this, here is code from the eShopOnWeb sample application. The Order entity is the Aggregate Root that is returned from a Repository.

The sample code has a Query that is using the IOrderRepository to list all the Orders for the logged-in user.

Since we don’t need the Orders Aggregate, we don’t really need to use the Repository Pattern. The benefit of not using the repository is rather we can select the data we actually need for this use case. In this sample, it was reusing the OrderViewModel to be used when listing all the Orders as well in another route when viewing an individual Order.

This re-use is actually not helpful because the Order Listing page does not need any of the Order Items or the Shipping Address.

Rather, we can define our result explicitly for this use case and fetch exactly the data needed. Again, this use case did not need any order items, the product for those order items, or the Shipping Address. The aggregate is fetching and returning all this data that we do not need.

The Repository Pattern

If I’m applying CQRS and Vertical Slices, it means that on the Command side I’m going to use a Repository to build up and return an Aggregate. An aggregate is a consistency boundary and is responsible for state changes that are controlled by invariants.

On the Query side, since I’m not making any state changes, I do not need an Aggregate. An aggregate is likely way more data that I likely need to transform into the result that I need to create. Queries are specific use cases in ways to return data. A Query is encapsulating that concept including how it’s accessing that data. You could decide to not even use the same library for underlying data access in your Repository as you are in any Queries. CQRS enables that option.

Source Code

Developer-level members of my YouTube channel or Patreon get access to the full source for any working demo application that I post on my blog or YouTube. Check out the YouTube Membership or Patreon for more info.

Related Links

Follow @CodeOpinion on Twitter

Software Architecture & Design

Get all my latest YouTube Vidoes and Blog Posts on Software Architecture & Design