STOP doing dogmatic Domain Driven Design

The mainstream thought on Domain Driven Design is about Entities, Value Objects, Aggregates, Repositories, Services, Factories… all kinds of technical patterns. Because of this, most don’t think they need Domain Driven Design because it’s complicated for their domain. Why would you need all that “stuff”? Well, maybe you don’t! In a large system, modeling your domain, defining boundaries, and how they relate is far more important than concerning yourself if you’re using the Repository pattern correctly.


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.

Domain Driven Design

I often use terms that people associate with Domain Driven Design (DDD), however, I generally don’t call out Domain Driven Design explicitly in many of my videos or blog posts. You probably have noticed that I do talk a lot about boundaries. Boundaries are probably the most important aspect for me that came from Domain Driven Design. However, the vast majority of content or discussions you will find online about DDD revolve around tactical patterns: Entities, Value Objects, Repositories, Services, Aggregates, etc.

While the tactical patterns have value, they must come after understanding the concept and value of defining boundaries with a Bounded Context. While a Bounded Context does get a lot of attention from Domain Driven Design enthusiasts, it’s not what people are introduced to first in tutorials or “sample” applications.

Here are some of the comments and questions I see the most related to Domain Driven Design:

DDD is only powerful when your business logic is complex

While I agree complexity is a driving factor, I also think it’s helped me most when dealing with larger systems. Complexity from the Domain as well as the complexity that comes from a large system.

In DDD you’re supposed to use a repository pattern.

You need to use factories to create entities or value objects.

This is the wrong concern. Focusing on patterns rather than boundaries and modeling the actual domain.

You have no logic in your entities, so that’s an anemic domain model, which is an anti-pattern.

It’s only an anti-pattern if you think you have a domain model but you really have a data model with transaction scripts. There isn’t anything wrong with starting with that and moving to a richer domain model as your understanding of the problem evolves.

You can’t have dependencies in your Domain Model

People go to incredible lengths to avoid have dependencies in their domain model. While again it’s a good practice to avoid dependencies and worth striving for, sometimes you actually need a dependency. Using Double Dispatch isn’t bad because you’re passing a dependency to a method and someone told you all dependencies must be injected via the constructor.

Dogmatic Domain Driven Design

I think all the questions above are losing sight of the benefits of Domain Driven Design. I don’t think concerning yourself solely along with tactical patterns and if they are being applied correctly is applying DDD. I think that’s applying dogmatic DDD.

To me doing Domain Driven Design is about understanding your domain, the language, the context of different people within it, the problem, and the solution space and trying to model it. And you’re not going to get it right at first as it will take time to iteratively build upon the insights you gain.

There’s value in the tactical patterns like Aggregates (which are a consistency boundary), but that shouldn’t be the focus. Just because you have repositories, aggregates, entities, doesn’t mean you’re doing Domain Driven Design. You just have a bunch of patterns.


I find language a great way to understand the boundaries and define a bounded context within a system. A great example of this was from Mel Conway on Twitter.

Depending on who you’re talking to in your Domain, they likely have a different context given the subdomain they are in or the role that they have.

Exactly as Mel is pointing out, context matters in how language and intent is used.

As an example, in a Transportation company that transports freight/goods, there are multiple subdomains. Recruitment is who is hiring drivers, making sure they have the proper license, compliances, etc. Operations are concerned about the actual shipments and the freight being picked up and delivered.

Both have the concept of a Vehicle. But both have very different concerns and views on what a Vehicle is. They share the same term “Vehicle” but based on their context have very different views of what matters to me.

STOP doing dogmatic Domain Driven Design

In recruitment, the concept of a Vehicle maybe is owned by a Driver (known as an Owner Operator). They might care about the Vehicle Safety requirements and other compliances. Operations care about the availability of the vehicle and if it can do a particular shipment at a given time. Very different concerns.


Understanding a Domain, Subdomains, and developing a Bounded Context can be very challenging. I like to use the analogy of walking into a dark room with only a small flashlight

At first, you have no mental model of what the room actually looks like. As you slowly flash the light around the room, you get a better mental model of how high the ceilings are, what’s the shape of the room, what’s on the floor. Your understanding grows slowly as you shine the light.

Understanding the boundaries of a domain takes effort and is the key to being able to understand the problem and solution space. This is the key to domain modeling.

Domain Driven Design

The title says it all. It’s not pattern-driven design, it’s Domain Driven Design. Don’t get caught up in the dogmatic majority that is focused on the tactical patterns. Again, yes they are valuable. Aggregate Design is a great way to define consistency boundaries. I’m not discounting the patterns, but that’s not the focus. The patterns are a means to an end.

Source Code

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.

Competing Consumers Pattern for Scalability

The Competing Consumers Pattern enables messages from Message Queues (or Topics) to be processed concurrently by multiple Consumers. This improves scalability, availability but also has some issues that you need to consider such as message ordering and moving the bottleneck.


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.

Competing Consumers Pattern

Message Queues are a great way to offload work to be that can be handled separately by another process. Meaning if you have a web application that takes an HTTP request, it can add a new message to the queue and then immediately return to the client. Since the work is being done asynchronously, the web application is non-blocking because the work is being done in another process.

In this example, the client/browser makes an HTTP call to the web application.

If the request itself can be done asynchronously and the client doesn’t need a consistent response, instead of performing the actual work, it creates a message.

That message is then sent to the message queue from the API, which is our Producer.

Then the API/Producer can return back to the client.

The actual work to be done will be handled by a Consumer. A consumer will get the message from the Message Queue and perform the work required.

As the consumer is processing the message, our API could still be creating new messages from HTTP Requests.

And producing more and more messages that are being put into the queue.

At this point, we have 3 messages in the queue. Once the consumer is done processing the message, it will go and grab the next message in the queue to process.

Competing Consumers Pattern

In this illustration, the rate at which we are producing messages and adding them to the queue exceeds how fast we can consume messages.

This may or not may be an issue depending on how quickly these messages need to be processed. If they are time-sensitive then we must increase our throughput.

To do that is simply to increase the number of consumers that are processing messages off the queue.

Competing Consumers Pattern

This is called the Competing Consumers Pattern because each consumer is competing for the next message in the queue.

If we have two Consumers that are both busy processing a message, and two messages waiting in the queue.

Competing Consumers Pattern

The moment one of the consumers finishes processing it’s message, it will get the next message in the queue.

Competing Consumers Pattern

Since the second consumer is free, it now gets the next message in the queue.

Competing Consumers Pattern

Increasing Throughput

The Competing Consumers Pattern allows you to horizontally scale by adding more consumers to process more messages concurrently. By increasing the number of consumers you will increase throughput and improve scalability and availability to manage the length of the queue.

Because the number of messages can fluctuate in most systems, you may see more messages during business hours than after hours. This allows you to scale the number of consumers and the resources they use. At peak hours you increase the number of deployed Consumers and off-peak hours you can reduce the number of consumers deployed.

Moving the Bottleneck

There are a couple issues with Competing Consumers Pattern and the first is moving the bottle neck.

If you have a lot of messages in the queue and you add more consumers to try and increase throughput, this means you’re going to be processing more work concurrently. This can have a negative effect downstream to any resources that are used in processing messages.

For example, if the messages being processed involved interacting with a database, you’ve moved the bottle neck to the database.

Competing Consumers Pattern

Any resources (database, caches, services) will all feel the effects of adding more consumers and processing more messages concurrently. Beware of moving the bottleneck that downstream resources can handle the increased load.

Message Ordering

The second common issue with the Competing Consumers Pattern is the expectation of processing messages in a particular order. For example, if the Producer is creating messages based on workflow the client is doing, then the messages are created in a particular order and added to the queue in a particular order.

Most message brokers support FIFO (First In First Out), which means that consumers will take the oldest message available to be processed.

This does not mean however that you won’t have correlated messages being processed at the same time.

Here’s an example of a yellow message being processed by a consumer.

Competing Consumers Pattern

If the producer produces another message that relates to the first message, and there are consumers available to process it, they will.

Competing Consumers Pattern

This can have negative consequences to your system if you’re expecting to process messages sequentially in a particular order. Although you will receive messages in order, that does not mean you will process them sequentially in a particular order because you’re processing messages concurrently.

Source Code

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.

Highly COHESIVE Software Design to tame Complexity

What is cohesion and why should you care? Highly cohesive software design can reduce complexity and coupling. But what is cohesion? It’s the degree to which the elements inside a module belong together. How you group operations together can have a widely different outcome on Cohesion. Informational Cohesion is grouped by operations on data. Functional Cohesion is grouped by operations of a task. It’s directly related to the Single Responsibility Principle, which you might also have a different definition of.


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.


Most people generally hear about Cohesion in relation to Coupling. I’ve done a post about how to Write Stable Code using Coupling Metrics, but I haven’t yet touched directly on Cohesion. To me, Cohesion and Coupling are like the yin and yang of software design.

To give it a simple definition:

“degree to which the elements inside a module belong together”

Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design

However, most people are likely more familiar with or think of the Single Responsibility Principle, which is directly related to Cohesion. Single Responsibility Principle states:

A class should have one, and only one, reason to change

 Agile Software Development, Principles, Patterns, and Practices

What what does that really mean? What is “one reason”? Does that mean that a class/module should only have one job? What about dependencies, what if they change? Doesn’t that affect other classes (coupling) and they have to change?

Robert C. Martin, the author wrote about this years ago on his blog to clarify.

When you write a software module, you want to make sure that when changes are requested, those changes can only originate from a single person, or rather, a single tightly coupled group of people representing a single narrowly defined business function. You want to isolate your modules from the complexities of the organization as a whole, and design your systems such that each module is responsible (responds to) the needs of just that one business function.

What’s funny about this is that most classes/modules are not organized by business function, but rather they are organized by data.

If you read any of my other posts, you know that I advocate thinking about business capabilities and not technical concerns, which I describe more in my post AVOID Entity Services by Focusing on Capabilities.

Informational Cohesion

I think the common practice is to organize operations (methods) in a class/module using an informational cohesion approach. Information cohesion is about grouping related to operations on data.

For example, a ProductService class that did data access for a Product Entity (Data Model).

These operations are grouped in this Interface/Class because they operate on the same information/data.

Functional Cohesion

Functional Cohesion is about grouping related to the operations of a task. Single Responsibility mentions are about a narrowly defined business function(s).

This means that you don’t group by Entity/Data/Information, but rather the boundary in which users perform actions within your system. In other words, more based on roles. Cohesive software design, to me, is focusing on functional cohesion.


To illustrate this more, here’s an example of a class that depends on a IProductService that I’ve defined above.

The problem with Informational Cohesion that is occurring in IProductService, is that we don’t require anything more than one single method (GetProductBySku).

To test this class, we have a couple of options. We can create a stub of the interface or we can mock it. Most would probably choose to use a mocking library so we only have to mock the one method.

However, if we change the actual implementation to use a different method on our IProductService interface, this test is going to fail because we haven’t mocked every method on the interface.

Do we really want a dependency on that interface so we can use one method? When you need a class for one method, you don’t really the interface, you want a function.

In C#, Delegates are… functions!

Instead of an interface, I’ve defined delegates within a static class that are specific for the Catalog.

Now instead of depending on the interface, we depend on the delegate.

Now our test becomes much more explicit in how we create this Handler to test. We need to stub the delegate. There is no mock. There is no mocking framework.

If we change the implementation to use a different delegate, if the signature of the delegate is the same, then the test will still pass. If the signature changes, then our code won’t even compile. We’re being as explicit as we can about what we depend on.

Cohesive Software Design

When I’m thinking about Cohesive Software Design, I’m thinking about grouping by business functions and capabilities. Grouping by tasks, by roles, but what the business process and workflows actually are.

By increasing cohesion you can reduce coupling.

Source Code

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.