Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.

What’s enough Complexity for a Domain Model?

Should you be applying domain-driven design? Do you need a domain model? You might want to if you have a lot of complexity within your domain. I often get asked, “what is a lot of complexity?”. I’m going to provide an example by uncovering more and more complexity within a domain. And give a few different insights that should help you make the decision.

YouTube

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

Mounting Complexity

Through this post, I’m going to illustrate an example using a shipment, moving freight/goods from one location to another. Everyone should be able to relate to this. You order something online and have it delivered to you.

Let’s start with the simplest form. First, a vehicle will arrive at the first stop, often called the shipper, and pick up the freight.

No Complexity: Pickup

Then the vehicle will drive to the destination, often called the consignee, and deliver the freight.

No Complexity: Delivery

When considering an order you place online, this would mean a vehicle goes and picks up your order/package at a warehouse or store, then delivers it to your house/apartment.

No Complexity: Model

On the surface, the complexity within this domain model might seem minimal. A shipment will be associated with a vehicle, pickup, and delivery location. In terms of behaviors, we would be able to arrive at the pickup location, pick up the freight, depart the pickup location, arrive at the delivery location, and deliver.

This is pretty simple at this point in terms of our conceptual model. In terms of domain logic, it would be around making sure we arrive at our pickup location before we arrive at the delivery. This means we are doing shipment steps in the correct order.

More Complexity

As we dig into the reality of this domain, the complexity within this domain model is about to go up drastically. A vehicle isn’t always going to pick up a single package and bring it directly to the delivery location. What may also occur is bringing the shipment to a warehouse before the final delivery.

More Complexity: pickup

This means that Vehicle A might pick up the freight from our pickup location and then deliver it to a warehouse or some intermediate location.

More Complexity: Warehouse Delivery

The freight might sit at this warehouse location for a period of time.

More Complexity: Warehouse

Finally, when the product needs to move to the delivery location, we could have Vehicle B arrive at the warehouse to pick up the freight.

More Complexity: warehouse pickup

Vehicle B then drives to our delivery location to deliver the freight to the final destination.

More Complexity: delivery

We’ve added a bit more complexity. Now we need to keep track of which vehicles are involved in our shipment.

More Complexity Model

Now with this model, we could have many intermediate pickups and deliveries as needed. For example, we may have two different warehouses involved before our final destination.

More Complexity

Now let’s say that we have a shipment that the freight is split into multiple packages that need to be delivered to multiple locations.

First, the vehicle picks up multiple packages at the pickup location.

Even More Complexity: pickup

It then brings the freight to the first delivery location and delivers one of the packages.

Even More Complexity: Delivery #1

Then it drives to the other delivery location to deliver the final package.

Even More Complexity: Delivery #2

Now, we need to keep track of the freight itself to determine which delivery location it belongs to.

Even More Complexity: Model

Complexity. It never ends.

Let’s combine those last two examples: a warehouse or intermediate stop and multiple delivery locations.

Endless Complexity

Now depending on the size of freight and the types of vehicles, it’s not very efficient to have one shipment per vehicle. So this means that we have multiple shipments on a vehicle. And those individual shipments may have multiple pickup and delivery locations.

Is this starting to sound complex yet? Modeling the data might not be the most challenging part. However, all the business logic of making sure you’re routing these stops in the most efficient order, making sure you’re going to be on on-time for pickup and delivery times required, can get incredibly complex.

The reality of this domain has a lot of complexity that I’m just scratching the surface of. The point is that what looks pretty simplistic on the surface of a single package with a single pickup location and delivery location, in reality, goes much deeper and has a lot of complexity you need to manage.

What’s enough Complexity?

Do you warrant a domain model to manage the complexity? Here are a few questions to ask yourself.

Is it CRUD (create, read, update, delete)? Honestly, is it CRUD? When you’re working with a system that’s primarily CRUD, all of the workflow and business processes are in the end-users heads. They are the ones executing the workflow and entering the various pieces of data. They decide if X happens, then they need to input data into Y. The end user understands the workflow, not the software.

If you’re building a system that captures the workflow and will contain all that business logic, then yes, creating a domain model can be very beneficial as a way of organizing all that logic and complexity.

How many capabilities does your system provide? If you’re trying to decide if you should capture the workflow in your software, how many capabilities does it need to provide? If it’s minimal, it might not be needed.

If it is very limited, will it grow? Will the number of capabilities grow over time? It may start very small, but as its value is realized, more and more features are added.

The example I gave at the beginning may be the 80% use case. In that case, maybe you don’t need a domain model. But you do for the other 20%, which has a lot more complexity.

Not all models need to solve all the problems.

If you don’t know if you have a lot of complexity, then you don’t know enough about the domain and the problem the system is trying to solve. If this is the case, explore more about the domain, the problem space, and what it is you’re trying to solve.

Join!

Developer-level members of my YouTube channel or Patreon get access to a private Discord server to chat with other developers about Software Architecture and Design and access to source code for any working demo application I post on my blog or YouTube. Check out my Patreon or YouTube Membership for more info.

Follow @CodeOpinion on Twitter

Software Architecture & Design

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

Which Software Architecture Patterns do you use?

What software architecture patterns do you use? If I asked this question, what would be your answer? I’d probably get a lot of responses that say Clean Architecture, and some people would say Microservices or a Monolith. But really, your software architecture is usually unique. In this video, I will cover how you buffet architecture to mix and match different architectural styles that fit together to make your specific architecture.

YouTube

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

Architectural Styles & Patterns

When you think of going to a buffet-style restaurant, you have all these different types of food available, and you likely pick a few different dishes to make your plate. The same is true with your software architecture. It’s not a single architecture but a composition of different architectural styles and patterns.

Here’s a menu of the most popular/familiar options. What are your needs and what are you choosing?

Software Architecture Patterns

Maybe you’re using a microservices architecture because of organizational concerns and the need for independently deployable and scalable services. You also have a lot of complex domain logic and you want to apply a clean architecture to manage coupling.

Software Architecture Patterns Microservices

Or perhaps you’re creating a monolith with well-defined logical boundaries. You’re using an event-driven architecture to handle asynchronous workflows between logical boundaries. Some logical boundaries might be using a clean architecture and also focusing on features, and using a vertical slice architecture as way to organize code.

Software Architecture Patterns Monolith

Monolith

That last example might be unfamiliar to some. You can have a monolith that isn’t a big ball of mud. It can be a combination of software architecture patterns. As mentioned, you can define explicit boundaries and loosely couple between them. Because not all boundaries have the same domain complexity, they might not all need to be organized the same way. Some might use vertical slices, and others might be more CRUD driven.

Loosely Coupled Monolith

I refer to this combination of architectural patterns as the Loosely Coupled Monolith.

Instead of coupling between types or making calls in-process between logical boundaries, you’re communicating via events. Your monolith is the producer and consumer of events.

One logical boundary can produce an event and send it to a topic on the broker when a particular business event occurs.

Producer

Other logical boundaries within the monolith can consume and react to that event asynchronously. This might be a part of a business process or used for communication.

Consumer

4+1 Architectural View Model

It’s important to remember there are different ways to look at your system. The 4+1 architectural view model illustrates this.

4+1 Architectural View Model

The fallacy in the current industry is thinking a logical view and a physical view are always the same.

Meaning a logical boundary (or service) must be independently deployable. This is not the case. Physical boundaries aren’t logical boundaries.

In my monolith example, multiple logical boundaries are hosted within the same process (physical). There are advantages and also disadvantages to this. But the point is they don’t have to be the same. They don’t need to be one-to-one.

This is important because if you’re loosely coupling between boundaries, then you could decide to host them all together (physical), or you may decide to carve off a logical boundary and deploy it independently.

Logical vs Physical Boundaries

Logical boundaries are also important because they aren’t all created equally. Not every logical boundary will have the same value to the overall system. Some boundaries might be the core of your domain and contain a lot of complexity. Other boundaries might be simpler with no actual domain logic and can be purely CRUD-driven. These are often more in a supporting role. Defining logical boundaries allows us to understand how we want to handle coupling and cohesion within a single boundary.

Logical Boundaries

In one boundary, we might use a vertical slice architecture with a more task-based UI that uses event sourcing and an event store. Another logical boundary might use an entirely different way of persisting state.

Mix and Match

When someone asks you what software architecture patterns you use, your answer likely is a mix and match of different architectural styles and patterns that make it unique based on your requirements.

I hear people shouting: “Just make it simple. Adding all these architectural patterns make things overly complex!”.

I’m not advocating making a heaping plate of architectural patterns for no reason! Be pragmatic and understand your needs. What is often perceived as “simplicity” can also lead to complexity, typically by forcing all use cases into the same mold and not adopting the approach that fits best.

Join!

Developer-level members of my YouTube channel or Patreon get access to a private Discord server to chat with other developers about Software Architecture and Design and access to source code for any working demo application I post on my blog or YouTube. Check out my Patreon or YouTube Membership for more info.

Follow @CodeOpinion on Twitter

Software Architecture & Design

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

Why is Clean Architecture so Popular?

You’ve probably noticed many videos and blogs that somewhat explain what Clean Architecture is and show you how to use it. So its Clean Architecture is popular, but should it be? Should you be using it? Here’s why I think it’s popular, the problems it addresses, and some aspects that almost nobody ever mentions.

YouTube

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

Clean Architecture

As a quick primer, what is clean architecture? Well, it’s a way to manage coupling. Specifically, in this diagram, you can see how the outer parts of the circle reference the inner parts of the circle. The dependencies between layers are pointing in a single direction inward.

It’s about managing coupling.

As an example, with the Clean Architecture template for .NET/C#, the project structure and dependencies are as follows.

The top (outer layer), called WebUI, is ASP.NET Core. It references an Infrastructure project that contains the entity framework DBContext and other concerns. The WebUI and Infrastructure reference the Application project, which contains the interfaces for implementations in the infrastructure and any application-level code, such as commands, queries, and handlers. Finally, the application project references the Domain project, which contains (or should) your domain models and business logic.

Sounds great. Separation of technical concerns. But why?

Coupling

degree of interdependence between software modules

ISO/IEC/IEEE 24765:2010
Systems and software engineering — Vocabulary

There are two forms of coupling Clean Architecture addresses. Afferent and Efferent.

Efferent Coupling: Who do you depend on? From the perspective of the Domain project, who does it depend on? Nothing.

Afferent Coupling: Who depends on you? From the same perspective of the domain project, which projects depends on it? The Application Project.

This is about stability. Because the Domain project has no dependencies, nothing can force it to change. All our business logic is isolated and cannot be forced to change because of a change within the infrastructure project or any other project. The reverse is true for WebUI. Changes we make in the infrastructure or Application could force us to make changes in the WebUI.

Do you need Clean Architecture?

It would be best if you asked yourself a few questions. What is the size of the application? Do I have complex domain logic? Do I need to control coupling?

Clean architecture is about forcing a direction of dependencies. In .NET, projects were used in the template above to force the separation. However, you do not need separate projects. Coupling is the dependence between types. If you merged the template into one project, you still have the same degree of coupling.

Do not use Clean Architecture as a prescription or template. Understand that you’re trying to manage coupling. It doesn’t need to be by projects. However, it can be to help with physical separation. It doesn’t need to be those exact layers. It’s not a prescription.

You should consider decomposing it into logical boundaries if you have a large system. What’s a large system? Something that takes a team of developers, possibly years to develop. I’ve covered this in many different blog posts and videos. Check out my post Microservices gets it WRONG defining Service Boundaries and Should you use Domain Driven Design? where I talk about logical boundaries. Logical boundaries are about grouping a cohesive set of capabilities within your system. It allows you to decompose a large system into smaller subsystems.

Why does this matter? When you break up a large system into smaller parts, you’ll realize that not all parts provide the same value. While all the boundaries are important, some are more in a supporting role and often built around CRUD (Create-Read-Update-Delete). This is also very similar if you’re creating a smaller app that may take a couple of weeks or months to develop.

If you have no domain logic, do you need to all the same layers as another part of your system that is at the core of the solution space and contains complex business logic? No.

This is why it’s not a prescription or template. Each boundary within a system has different concerns. If you don’t have any business rules, you have an underlying data model. Or perhaps you only have a dozen or so routes/endpoints that have data access. Do you need to add an abstraction to data access in that case? What if your database changes? Then change the 12 or so routes/endpoints!

Clean Architecture

Clean architecture is about coupling. There’s no prescription for the layers you define or how you define the coupling. You don’t need to define layers by projects. It’s about the direction of dependencies between types. Afferent and Efferent coupling are what define the stability of each layer. Do you need stability in a particular layer? Then maybe consider isolating it.

Join!

Developer-level members of my YouTube channel or Patreon get access to a private Discord server to chat with other developers about Software Architecture and Design and access to source code for any working demo application I post on my blog or YouTube. Check out my Patreon or YouTube Membership for more info.

Follow @CodeOpinion on Twitter

Software Architecture & Design

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