Message Driven Architecture to DECOUPLE a Monolith

Message driven architecture doesn’t need to apply just to Microservices. You can apply a Message Driven Architecture to a Monolith to decouple concerns. Consuming Commands and Events to handle concerns into their isolated units. The asynchrony and isolation will also enable your application to be more reliable and resilient.

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.

Monolith

Just because you’re developing a monolith, doesn’t mean you can’t leverage a message driven architecture. Messaging isn’t simply about communicating with other services. You can use messaging to communicate with your own monolith.

Typically we think of a monolith as a single application that’s singularly deployed (maybe many instances) that interacts with a database. Yes, there may be some other resources such as a cache or 3rd party APIs but for the most part, people think of it as a simple diagram:

There are two different aspects to a message driven architecture we can use to decouple various concerns within our system. Commands and Events, which are both types of Messages. The terms Producer and Consumer are often used. A producer is creating messages and sending them to the message broker while the consumer is receiving and processing the messages. In the case of a monolith, it will be both the producer and the consumer.

One thing to be aware of is that using a message broker is moving work to be asynchronous. Meaning you’re pushing work that was previously all done together to now be done separately in isolation. This isolation is what gives you resiliency and reliability to your system, however, it comes at the cost of a mind shift of being able to realize work that can be done asynchronously.

Publish/Subscribe to Events

First, the publish/subscribe pattern is when the publisher publishes an event to a message broker. The publisher is totally unaware if there are any consumers. There may be no consumers, there may be many.

Above we’ve published a message to the message broker from our monolith. If we had 3 consumers that want to process that event, that message would be delivered back to our monolith to all three consumers within it.

To illustrate the utility of this within a monolith, let’s say you have an e-commerce site. When a customer places an order, you must save the data to your database as well as send the customer a confirmation email.

Saving the data and sending the email are two different concerns. If these are done together in the same process, there are various ways this could fail.

What if the order is saved, but the email service is unavailable? Will the email never get sent? Will the customer see an unexpected error? Sure you could wrap these calls in retries as well as handle the exceptions, but you’re adding a lot of complexity to handle faults simply because you have two concerns that are mixed.

In this case, when the customer places an order, you would publish an OrderPlacedEvent to the message broker. You would then asynchronously be consuming that event back in your monolith, which would be processing that event to send the email.

In this case, if your email service is unavailable, the order will still be persisted, however, the email will not get sent. Most messaging libraries support ways to manage retries upon failures, exponential backoffs, and moving messages to a dead letter queue. Check out my post on Handling Failures in Message Driven Architecture.

In many transient failures often a retry will be successful. This means that if the consumer failed to process the message it will try again and likely succeed. In the case of our email service, if it had a transient fault, once we retry it may be successful. In this case, we never lost the order, the customer didn’t see an unexpected error, and the confirmation email was eventually sent.

Enqueue Commands

Another way we can leverage messaging is by sending commands to a queue. This is different from publish/subscribe as every command has one single consumer.

We can leverage a queue by creating commands for various work that can be done asynchronously. As mentioned earlier, the shift is moving from a synchronous workflow to an asynchronous one can bit a bit of a mind shift. As an example, when a customer is placing their order on our site, do we really need to persist it to the database? What if we simply created a command that we send to the message broker/queue and then immediately return a response back to the customer that their order was placed?

If we have a high-traffic site and our database gets overwhelmed, we won’t lose any orders as customers are placing them, nor will our customers see any unexpected/database errors. We are simply creating messages and sending them to the queue to be processed.

The isolation of consuming messages gives us reliability and resilience but it also allows us to scale. Check out my post on the Competing Consumers Pattern for Scalability

Asyncrony of Message Driven Architecture

The biggest concern with messaging is understanding where and when you can move work to be asynchronous. If a user clicks a button and expects a specific result to be returned back, that can only be done if the work is synchronous. However, there are many instances where this comes down to expectations.

For example, if you are in a cloud provider’s portal and stop a virtual machine. Their UI will update saying the virtual machine is stopping. It didn’t instantly stop. They are using the same methodology of queuing work to stop the virtual machine but make you the end-user aware that it’s in progress and not fully complete.

Understand where you can move work to be asynchronous and what if you need to set users’ expectations about when it occurs.

Source Code

The YouTube video that covers this post shows actual code and a demo application. Developer-level members of my CodeOpinion YouTube channel get access to the full source for any working demo application that I post on my blog or YouTube. Check out the membership for more info.

Related Posts

Follow @CodeOpinion on Twitter

Enjoy this post? Subscribe!

Subscribe to our weekly Newsletter and stay tuned.

Does CAP Theorem apply to Microservices?

Microservices are a distributed system so CAP Theorem must apply right? Well, that depends on how you define microservices and a distributed system! Microservices are defined as “loosely coupled services oriented architecture with bounded context”. SOA implies Event Driven Architecture. If your microservices are loosely coupled, they aren’t making direct RPC calls to each other. If this is the case, then there are no partitions that can occur between services. If services are a bounded context and own their own data, then there is no consistency between services.

CAP does not apply to Microservices if they are loosely coupled and are a bounded context.

Here’s a breakdown and to further explain why the question “Does CAP Theorem apply to Microservices?” doesn’t even make sense and has no relation to Microservices.

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.

CAP Theorem

In theoretical computer science, the CAP theorem, also named Brewer’s theorem after computer scientist Eric Brewer, states that it is impossible for a distributed data store to simultaneously provide more than two out of the following three guarantees:

Consistency: Every read receives the most recent write or an error
Availability: Every request receives a (non-error) response, without the guarantee that it contains the most recent write
Partition tolerance: The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes
When a network partition failure happens should we decide to

Cancel the operation and thus decrease the availability but ensure consistency
Proceed with the operation and thus provide availability but risk inconsistency
The CAP theorem implies that in the presence of a network partition, one has to choose between consistency and availability.

https://en.wikipedia.org/wiki/CAP_theorem

An example I often see which explains CAP pretty well is the use case of an ATM. Let’s say there are 2 ATMs that communicate over the network. Anytime you go to deposit money into an ATM, it confirms the other ATM is online and available.

If both ATMs are online and available and you make a deposit of $100, both ATMs will have recorded that deposit. If you go to the other ATM and check your balance it will be $100. Both ATMs are consistent.

When a network partition occurs, we must choose consistency or availability.

If we choose consistency, then if we cannot communicate between ATMs, then we cannot allow a deposit at one ATM because it will not be consistent with the other ATM.

If we choose availability, then we will allow the deposit to occur, however, if we go to the other ATM, it won’t be aware of our $100 deposit. Our system would have to reconcile the differences between ATMs when they are able to communicate.

So back to the question: Does CAP Theorem apply to Microservices? Well, what’s the definition of Microservices?

Microservices

Adrian Cockcroft defined Microservices as:

loosely coupled service oriented architecture with bounded contexts

https://www.slideshare.net/adriancockcroft/dockercon-state-of-the-art-in-microservices

This is very different than a distributed system where each node provides the same capabilities and data. Microservices are about the decomposition of a larger system. Each microservice has its own behavior and its own data. Comparing the two systems are like comparing apples and oranges.

But there are two key points that I’d like to talk about in Adrian’s definition that really drive this home. Loosely coupled and bounded context.

Bounded Context

The concept of a bounded context from Domain Driven Design is about creating a boundary. Adrian says:

If you have to know too much about surrounding services you don’t have a bounded context.

This is because concepts should be owned by a particular bounded context within a subdomain. One service shouldn’t have to know explicitly about the details of other services. Services are about defining business capabilities and the data behind those capabilities.

This is very different from the ATM example because again, each service is defining its own capabilities (functionality). With these capabilities is data ownership. Data isn’t owned by multiple bounded contexts. Building services with high functional cohesion is key in defining service boundaries.

Loosely Coupled

Coupling can be thought of in two ways. Afferent and Efferent Coupling.

If you’re thinking about a module/class/project:

Afferent Coupling (Ca) is what other modules depend on it.
Efferent Coupling (Ce) is what other modules does it depend on.

In this example, the Catalog module has an Afferent Coupling (Ca) of 3 because Warehouse, Sales, and Billing depend on it. The Catalog module has an Efferent Couling (Ce) of 0 because it depends on no other modules. Warehouse, Sales, and Billing all have an Afferent Coupling of 0 because no other module depends on them and an Efferent Coupling of 1 because they all depend on the Catalog.

If your modules are communicating in-process (monolith) over the network via an HTTP, they are still tightly coupled. Again, communicating via a REST HTTP API does not make your services less coupled or loosely coupled. This simply makes your system a distributed monolith.

What is loose coupling?

In computing and systems design a loosely coupled system is one in which each of its components has, or makes use of, little or no knowledge of the definitions of other separate components. Subareas include the coupling of classes, interfaces, data, and services. Loose coupling is the opposite of tight coupling.

https://en.wikipedia.org/wiki/Loose_coupling

If you think back to Adrian’s comment about a bounded context, about knowing too much, this is what he’s referring to. A way to achieve loose coupling is through a Message or Event Driven Architecture.

Instead of our services communicating directly with other services, we are sending and publishing events to a message broker. Each module consumes Events and reacts to them. The publisher of the events is totally unaware of who the consumers are or if there are any consumers at all.

As an example in a long-running process of an e-commerce site of an order being placed, Event Choreography is used.

The first step is when an order is placed in the Sales service, an Order Placed Event is published to the message broker.

The Billing Service will consume that message and create an Invoice or Charge the customer.

Once the Invoice or Customer has been charged the Billing service will publish an Order Billed event.

The Warehouse service will consume the Order Billed event in order to allocate product in the Warehouse.

Once the product has been allocated and a shipping label has been created, it publishes a Shipping Label Created Event.

The Sales service consumes the Shipping Label Created event to update its Order Status.

All of this workflow was done using Event Choregraphy. No service knew about the other services consuming the messages it was publishing. They are loosely coupled.

This means that Event Driven Architecture is a characteristic of a Microservices architecture.

For more on how this works for long-running business processes that span services can be managed check out my post on Event Choreography or Orchestration.

Does CAP Theorem apply to Microservices?

No. Absolutely not. Not when talking about Microservices as a part of a larger system.

Yes if you’re talking about the context of individual service. CAP can apply to individual service and if it chooses consistency or availability.

Services are about defining a set of business capabilities. Services do not share ownership over data. Services are loosely coupled through a Message and/or Event Driven Architecture.

There is no concern for consistency because there’s no data that needs to be consistent between services.
There is no concern for availability because each service is autonomous.
There is no concern for partition tolerance because each service is autonomous and loosely coupled.

Microservices are about the decomposition of a large system.

Related Posts

Follow @CodeOpinion on Twitter

Enjoy this post? Subscribe!

Subscribe to our weekly Newsletter and stay tuned.

Clean Architecture Example & Breakdown

Clean Architecture is pretty popular and I’ve been asked a lot about it directly and indirectly. I decided to take a Clean Architecture Example (Template) and give my thoughts about where I see value in using it, in which context, and what I don’t like about Clean Architecture and/or how it’s organized, in practice.

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.

Clean Architecture

Many people have asked me for my opinion on Clean Architecture. They have also pointed me to Jason Taylors C# Clean Architecture Template. So here’s my breakdown of what I think about Clean Architecture, Templates, where I see the value, and where I don’t.

First let me cover what Clean Architecture is, for those unfamiliar. At the very middle is Entities that represent the core of your domain. This is where your business rules (and/or domain model) should live. A layer outside of that is Use cases, this is your application layer that is invoking your domain. Often times you will also define interfaces here that your application logic will use. The next layer outside of that is generally for defining implementation for those interfaces defined in the uses cases layer. These can be things like implementation for data access, file system, etc. Lastly, the outermost layer is for things like your UI/web framework which is the input into the system.

The key to all of this no matter how you break apart the responsibility of layers is about the direction of dependencies.

It’s about coupling and managing it. The very inner core of your domain will have no dependencies. While the outer layer will depend on one or many of the inner layers. If you’re familiar with afferent and efferent coupling, the core has an efferent coupling of 0 and an afferent coupling of 1. As you go out the reverse starts to occur where the outermost layer has an efferent coupling of 1 or more and an afferent coupling of 0.

Templates

This post (and video) are not a criticism of the Clean Architecture Template. I have a love/hate relationship with templates because it’s really difficult to provide context for when something should or shouldn’t be used. The problem with templates and sample applications is they are simple. Most applications may start simple but often grow over time along with complexity. Because applications evolve and grow, what was being done in a template may no longer fit what you’re currently building.

I also don’t think that a template even fits as a starting point for a new application because unless you are sure it fits the context of what you’re going to build. Speaking directly about this template, if you’re creating a simple application, I don’t think it even makes sense to use this template. Here’s why…

Project Breakdown

The template is broken down into 4 projects to represent the various layers and enforcing the direction of dependencies. The 4 projects in the template are Domain, Application, Infrastructure, WebUI.

Domain

The domain project is the core of the application. It does not have any other project references. Inside of it as Entities which are ultimately used by Entity Framework. As mentioned, templates are simple. Because of this, this simple template is using a Todo list as an example. There’s zero logic related to a to-do list sample. This means that the domain entities are really just data buckets that are representing our data model. They have no behavior. I would suspect in a complex domain that you would likely Aggregate with Entities and actual domain logic. But because this template is simple, as expected, you wouldn’t be aware that’s where the actual domain logic would live. It’s not a place to store data models.

Application

The Application project references the domain Project. This project is using MediatR to define commands, queries, and their respective handlers. These handlers are what are using the Entities from the Domain Project. This project also defines interfaces (abstractions) that are used for things like Data Access inside the handlers. However, the implementation for the interfaces lives in the Infrastructure Project.

Here’s the IApplicationDbContext defined.

Infrastructure

This project contains the implementation for the interfaces defined in the Application Project. This template it’s using Entity Framework for data access. There is an ApplicationDbContext that implements the IApplicationDbContext as well as extends the Entity Framework DbContext.

This means that your Application project code doesn’t use an Entity Framework DbContext directly, but uses the IApplicationDbContext.

However, if you look at the IApplicationDbContext snippet above, you will see that you have a leaky abstraction because we’re exposing the DbSet<T> from Entity Framework. This also means that the Application project also has a reference to Entity Framework. To me, this defeats the purpose of the separation of projects/layers.

WebUI

The last outermost project is what is hosting ASP.NET Core. Mainly there are Controllers that use MediatR to convert an HTTP Request into a Command or Query and then are dispatched via MediatR in the Application Project. I do like this approach because it makes a clear distinction between a web request and an application request. Let ASP.NET Core handle web-specific concerns and our Application project won’t have any of those concerns.

However, I do not like the idea of having a Controller with many different actions. Controllers have very low cohesion. You will also notice if you use a command/query dispatcher like MediatR that you likely will have in most cases a 1 to 1 mapping between a Controller action and the actual Handler. In other words, you are only going to create the request object in a single controller action. Because of this, I’d much rather have the controller action live alongside the command and handler.

Vertical Slices

That last statement is what drives me to the use of vertical slices. The flow starts to seem natural of a web request being invoked by ASP.NET Core in a Controller Action, which is then creating an Application Request (Command or Query) and then dispatched to a Handler. Some slices will share Infrastructure, for example, a DbContext, and share the same domain model.

What this looks like is cutting out a vertical strip from all the layers. Taking all the concerns for each layer and organizing your code that way.

When moving to vertical slices, you begin to stop thinking about layers and abstractions. The reason being is that a narrow vertical slice doesn’t necessarily need an abstraction. If you want to use your Entity Framework DbContext instead of an interface (abstraction) you can do so that dependency is in a limited number of other slices. If you want to change the implementation to something other than Entity Framework, for example, move to Dapper, you can re-write the data access in the limited number of places as required. You don’t have to swap out the entire layer, you swap out the features/slices you want.

Abstractions become less of a concern and you can start leveraging concrete implementations. It’s worth noting that anytime you create your own abstraction you’re likely going to lose functionality.

Clean Architecture

I don’t have any issues with clean architecture. It’s about coupling and managing it. However, you can also manage coupling by creating specific vertical slices of functionality. Each vertical slice defining its own dependencies (coupling).

Related Posts

Follow @CodeOpinion on Twitter

Enjoy this post? Subscribe!

Subscribe to our weekly Newsletter and stay tuned.