Fractional Architect

Mastering Strategic Domain-Driven Design – 5. Bounded Contexts

Cover Image for Mastering Strategic Domain-Driven Design – 5. Bounded Contexts
Maciej 'MJ' Jedrzejewski - Fractional Architect
Maciej 'MJ' Jedrzejewski - Fractional Architect

One of the most important steps in strategic Domain-Driven design is to organise your subdomains within special boundaries that are called Bounded Contexts. This is one of the most misunderstood concepts in DDD and my goal here is to make it as clear as possible to you. To do this, we will start with the simplest possible example of a bounded context and go deeper and deeper.

Imagine a city. In this city, there are different areas like a shopping district, a residential area, and an industrial zone. Each area has its own rules and purposes. For example, in the shopping district, you can buy clothes and toys, but you can't do that in the industrial zone, where factories make things.

Now, think of each of these areas as a bounded context. It is like having an invisible fence around each area that sets the rules and activities that can happen inside it. In a shopping district, the language and actions are all about shopping. In the industrial zone, the language and actions are about manufacturing etc.

Each bounded context has its own rules, language, and purpose. So, in general bounded contexts help people and systems keep things organised and make sense of all the different information and ideas they have to deal with.

In Chapter 4, we were able to distill subdomains that represent parts of a concrete business domain - in our case, Fitness Studio. The following subdomains were discovered:

  • Offers
  • Contracts
  • Passes
  • Reports

Using domain charts, each subdomain was assigned to a specific type - Core, Supporting or Generic. Thanks to this, we are ready to start thinking about the boundaries around it.

In the beginning of a project there are usually 2 situations that may occur:

Each subdomain is treated as a separate bounded context because there is nothing in common between them. This means that Offers subdomain will become the Offers bounded context, Contracts subdomain will become the Contracts bounded context etc.

Multiple subdomains are wrapped by a single bounded context. This can happen when subdomains are so close together that it makes no sense to wrap them in separate bounded contexts. Imagine there are 3 subdomains of Assessments, Progress Tracking and Virtual Coaching. You have discovered this:

  • terms and concepts such as trainee, coach, assessment, progress have the same meaning in all subdomains
  • the rules, workflows and processes are consistent across the subdomains. For example, if progress is measured in one way in one subdomain, it should be measured in a similar way in others, unless there is a compelling reason to differ
  • It is common for these subdomains to communicate with each other

And that is an indicator that perhaps it makes sense to put them in a single boundary called Personalised Training.

Perhaps.

Remember how during the Process Level Event Storming we looked deeply into the draft subdomains and made specific changes based on policies, actors and processes to create the final version of our subdomains? You can do the same with your bounded context drafts using these methods:

  • Bounded Context Canvas prepared by the great DDD crew (what would the world be without them? :))
  • Bounded Context Event Storming, proposed by Radek Maziarka as a step between process-level and design-level event storming. The main problem it addresses is that at process-level we focus on the whole process and map the workflow of the whole system - we do not want to focus on the details of a concrete bounded context, and at design-level our focus goes to aggregates and data modelling. So, if we want to show the workflows and policies within (and at the boundaries of) each bounded context, we need a step between the two. IMPORTANT: You can still handle it inside process-level but this way there will be a lot of work coupled with just one level of ES.

In this chapter I want to show you how to use the first one - the Bounded Context Canvas. Let's have a look at it:

Empty Bounded Context Canvas

The Name is obvious (or maybe not?) and represents the name of your bounded context. Next is a field that often provokes thoughts like Do I really need it? or What if we combine it with something else? which is called the Purpose. Then the Strategic Classification which combines 3 areas:

  • Domain Type - the same as for subdomains. If there is a 1-1 mapping of a subdomain to a bounded context, it is simple - a subdomain of a Core type becomes a bounded context of a Core type. If you have several subdomains of different types, e.g. Core and Supporting, the bounded context will be Core, if Supporting and Generic, then it will be Supporting etc.
  • Business model - does this bounded context bring you revenue directly? Or maybe users interact with it a lot and use your application because of it? Or does it protect your business reputation? Or maybe it reduces costs?
  • Evolution - is your bounded context already explored and there are off-the-shelf solutions you can buy for it? Or is it in an emerging market and you need to build it yourself? You can use the Wardley Maps to help you evaluate.

When you are ready with the strategic classification, the next important step is to define the Domain Roles of your bounded context. Would it receive a lot of data that would then be analysed to provide some context to your business or users? Or will it be treated as an execution context, triggering workflows and other bounded contexts?

The most important part is to define Inbound and Outbound communication. Starting with the former, you need to define who will communicate with this bounded context. It can be:

  • another bounded context
  • external system
  • user
  • frontend application etc.

Any of the above can send Query, Command or Event. Then based on policies and business rules, your bounded context processes the information and can send it to:

  • another bounded context
  • external system
  • user
  • frontend application etc.

by using Query, Command or Event. And this type of action you define in the Outbound communication area.

Assumptions and open questions about your bounded context can be written in the last section of the bounded context canvas. It is a good behaviour to also define some verification metrics. This allows you to validate the structure of the selected bounded context.

Enough theory, let's see what it looks like in practice. We experiment with one of our subdomains - the most complicated one - Contracts:

Filled Bounded Context Canvas

It is a Core bounded context, that is used for revenue. We decided to make it custom-built as the market is forming and it is seen as a competitive advantage over the others. It will be used for the execution and triggering other workflows.

What you can see as well is that this bounded context is triggered from the outside by a frontend application with 2 commands:

  • Prepare contract
  • Sign contract

where each is an entry point to a business process. Each process is restricted by a swimming lane (-----) to make it clear which business rules, inbound and outbound communication belong to which business process.

There are policies like Customer must be at least 18 YO that must be passed to finish the process. In the end, 2 events are sent to another bounded contexts (Risk Assessment and Passes) and 2 commands to the frontend application and external system (SAP).

Thanks to this approach, you are able to validate your ideas about bounded contexts. Very often, when you are working on them, you realise that they don't make sense. Especially at the beginning of the DDD adventure - sometimes this is very demotivating, because we have to invest a lot of time and work. As time goes by, you will see that bounded contexts are repeated in different systems (you will spot archetypes), and you will be able to reject some of them on the spot, and you will start to enjoy it.

When it comes to representing bounded contexts in code, using .NET as an example, I can recommend you map them directly to modules.

This means that 1 bounded context = 1 module in a modular monolith. In this way, you are able to extract one module (or a part of it) into a separate deployment unit (e.g. a microservice) very easily when needed.

If you are interested in this approach, here are links to 2 repositories worth checking out:

NOTE: This is just my recommendation. There are other approaches - 1 subdomain per 1 module (or microservice), 1 business process per microservice, etc. Based on my own experience and the products I have worked on, the approach I have mentioned works best for us (but that doesn't mean it can't be different for you). Let us always remember to respect other approaches and each other! :)

Once we have established the boundaries around our subdomains and wrapped them in bounded contexts, it is time for the final step of strategic DDD - establishing communication between multiple bounded contexts and external systems.

This is called Context Map and is our final focus in this series.


FREE Case Study: How did I introduce Domain-Driven Design in large scale organisation? Book an appointment with me to know more!