#14 Feel The Difference: Bounded Contexts vs Subdomains
Problem versus solution space? One-to-one mapping? Can you create bounded contexts per company department? Finally, is there any difference at all? Let's figure it out together.
I won’t start by describing the title issues.
Imagine a large-scale company - it can be the one you work for or any other. There are various departments:
HR
Finance and Accounting
Logistics
Marketing and Sales
Legal and Compliance
And more. Each department is responsible for its operations, has specialties, and, most often, some hierarchy.
To make it more abstract, think about a cake. The cake represents the entire company, but each slice is a representation of a specific department:
When you decide to hire people, you go to HR, provide all the requirements, and they start organizing the recruitment process. When you need to prepare the business contract for one of the third parties, the Legal department will handle it.
You do not go to Marketing to ask about the salary raise. Instead, you go to Finance.
There are clear boundaries around capabilities and responsibilities.
Let's return to the real world. Here, processes usually go through multiple departments. To hire someone, first, you must decide inside the development team that you need another developer. Next, it has to be approved by one (or many) mid-managers. To approve it, such a manager must contact someone from Finances to request a budget. After he gets the confirmation, he can ask HR to start the recruitment process. When the candidate is found, Legal must prepare a contract that is then signed.
Summing up, to hire a new developer, the process had to cross:
Development Team
Mid-managers
Finances
HR
Legal
Application development
You were asked to implement a system to help organize various processes in your company.
The decision on how to structure your software was easy. You and your team decided to modularize the solution so that each module represents a department. The assumption is that processes can be closed within each department boundary.
The first process you decided to implement is the one described before—hiring a new developer. The logic to get approval from the mid-manager (inside the mid-managers module) and the logic to get the budget is inside the Finances module. The HR module handles the recruitment process, and the Legal module is for contract preparation and signature.
To handle one process, it has to go through several modules—a lot of inter-module communication.
In case of modular monolith that communicates without using any external components like message broker, it won’t technically hurt that much as the entire application runs in a single process and communicates either by direct reference to a module (or its interface) or via in-memory queue. However, in a distributed system like microservices, where the entire inter-service communication is done through the network, there is a potential for a lot of problems (latency, network errors, partitions).
Another thing is that each department might call the person who will be hired differently:
Developer - in the Development team context
Resource - in the Mid-managers context
Unit - in the Finances context
Candidate - in the HR context
Entity - in the Legal context
Each time you leave the module, you start to use a different language. Imagine how many misunderstandings this can create.
Add the usual chaos in large-scale organizations, and you have a ready-made recipe for disaster in the form of a legacy system.
It was described in 1967 by Melvin Conway. It is known as Conway’s Law:
Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure.
Subdomains
To avoid such a situation, you should start with business discovery. This will help you understand your company's business capabilities, processes, and how it all works.
Remember the cake that represented your company and the slices that represented departments? The same abstraction can be used to represent:
Domain (a cake). It contains all processes that represent the business capabilities of your company
Subdomain (a slice). Each subdomain can represent one or several closely related processes focused on a specific purpose or business capability (high cohesion). It is also possible that some processes inside a selected subdomain are part of a larger process that touches several subdomains
A subdomain exists in the real world and shows how your business operates in a given area. You discover and explore them during discussions with domain experts, stakeholders, and other people with business knowledge.
You can do it, for example, by using Big Picture and Process Level Event Storming or Domain Storytelling. At that point in time, there are no discussions about technical solutions - no modules, modular monolith, or microservices. Nada.
Pure business exploration.
This way, you can find out that the process of hiring a new developer is the same as hiring any other role, like an accountant or project manager, and you can wrap all of it in a single subdomain called Hiring Employees.
This subdomain will cover the entire process of hiring:
Sending a request for a new developer
Receiving budget
Resource approval
Requesting new recruitment to HR
Preparation and signature of a contract
This way, you grouped all real-world actions (people must perform them to hire a new employee) and named this group (the Hiring Employees subdomain).
As mentioned above, there is also a chance that one of the processes that exists in a subdomain will be a part of the larger process. Given the Patient Treatment process, it requires:
Calling the front desk, confirming the appointment with one of the doctors, and scheduling the appointment. This process can be wrapped into the Appointment Scheduling subdomain
Examination, preparation of treatment plan, and providing drug prescription. This process can be a part of the Visit Handling
Preparation and sending the invoice to the patient. This process can be a part of Invoicing
So, in such a case, three subdomains will build the entire process of Patient Treatment.
Bounded Contexts
We move away from the real world. We have to represent subdomains in our software; as you know, software is something abstract. You cannot touch it.
What to do then?
You have to create a model for it. One of the ideas is to create one unified model for all distilled subdomains. You start doing it and then find out that there is various terminology (candidate, employee, resource, developer, entity) that you need to distinguish somehow (can end up in quite confusing ifology and mappings).
Furthermore, there might be the same words that have completely different meanings. For example, when talking about capital, it will mean something else in the context of geography - capital of a given country - compared to the financial world - assets or resources used to start and operate a business. Every single time, you have to check if it is in context A or B. If you continue this way, you will probably end up with a big ball of mud.
That’s why it is better to divide the model into multiple ones. That’s how you create bounded contexts. They divide your system into parts, each representing a unified model around a selected, cohesive area. It operates on a ubiquitous language; e.g., capital will always mean the same within its boundary.
So, what’s the difference between subdomain and bounded context?
The funny fact is that in most cases when building a system from scratch, subdomains are mapped 1:1 to bounded contexts. So, the Hiring Employees subdomain becomes the Hiring Employees bounded context.
Sometimes it makes sense to combine multiple subdomains into one bounded context. For example, when two or more subdomains use identical terminology and have significant interdependencies.
Now, you can focus on defining strategic classification, business policies, inbound and outbound communication (how the bounded context is triggered and what it triggers), and much more inside each bounded context. You can use, e.g., Bounded Context Canvas from DDD Crew, which will help you with that. Next, you can define the relationships between multiple bounded contexts (Context Map). More about it in my DDD booklet).
Over time, bounded contexts will evolve. Maybe, in the beginning, there was only one team responsible for a selected bounded context. However, now there are two because you want to extend your bounded context with additional capability.
Imagine that you work for a company that offers home insurance. Let’s assume that you created an Insuring bounded context to simplify the case. It worked great for several months. However, your company has acquired a competitor that sells health insurance. The company hired another team to integrate it into the existing Insuring context.
There is one problem. Both insurances are quite different - business rules, terminology, and actors. Each team pulls this bounded context into their direction:
If this continues that way, sooner or later, you will face a lot of issues. That’s why creating a separate bounded context might make more sense than integrating health insurance into the existing one. This way, you will have two bounded contexts responsible for the insurance:
Of course, the same can happen when there is an existing bounded context, and it grows without another company acquisition. Imagine having a bounded context for Appointment Scheduling. It allows customers to schedule appointments. There is a new requirement to add simple reminder functionality—24 hours before the appointment, the customer receives a reminder email.
As it is quite a small extension, it makes sense to integrate it into Appointment Scheduling to keep it simple. However, after several months, it starts growing. It is possible to add custom reminders, manage existing ones, remove them, etc. At some point, you should distill it to a separate one, maybe Appointment Reminding:
Transferring bounded contexts to code
How can you transfer knowledge from bounded contexts to your solution? There are some recommendations; I will show you my favorite one.
When you build your system around a modular monolith, there is a high chance that each bounded context will be represented by a module. Note that after some time, you might spot that it makes sense to extract part of it to another one (because of growth or mistake - bounded contexts like to change a lot in the first months of the application).
When you build your system around microservices, in most cases, each bounded context will be represented by a separate microservice. However, this is not always the case. Sometimes, it makes sense to have, e.g., two bounded contexts in a single microservice when they are related to each other, often communicating, and an additional point when one of the bounded contexts is relatively small. In a modular monolith, the application runs in a single process, and modules can communicate without needing a network connection. In microservices, each of them has to communicate with another over the network (independent processes). That’s why sometimes it makes sense to simplify the communication.
Recap
Subdomains help us understand complex business domains by dividing them into smaller parts. They represent real-world business capabilities wrapped into cohesive groups.
Bounded contexts represent independent, unified models, which allow us to transfer knowledge from subdomains into our technical solutions.