Integrating Bounded Contexts for DDD Beginners
Bounded contexts define a logical boundary of a business domain where a consistent Ubiquitous Language is used. Although bounded contexts evolve independently, they must still collaborate to create a system that will provide value for businesses.
Photo by Karolina Grabowska: https://www.pexels.com/photo/carton-boxes-and-stacked-books-on-table-4498124/
When I first started with DDD, one thing kept bugging me. What is the best way bounded contexts should collaborate? Should they evolve independently and duplicate components as they see fit? Should they have a shared set of classes that they use? Should you protect a bounded context from the contracts of another? I admit I was a little naive then and usually opted for a black-or-white approach. However, as I matured and read more resources, I concluded that there is no 100% correct or wrong way for bounded context collaboration. It all comes down to your business needs and how well the people working in various contexts communicate. In this article, I will share some common patterns for bounded context communication and when you should consider them.
Partnership
In this model, the integration between bounded contexts is done on the fly. If a team decides it needs to modify its API, it will inform the other teams, and they will make the necessary changes. No drama, no fuss. In this scenario, no team dictates the ubiquitous language that is used. They cooperate and find the most appropriate solution. Teams also work together to solve any integration conflict that might arise rapidly.
This pattern works best for mature teams that communicate well. Conversely, teams distributed across large geographical areas might find this approach challenging as communication and synchronization are more complicated and time-consuming.
The partnership model
Shared Kernel
In real life, there are a lot of situations where some models are used across many bounded contexts. When this happens, you have two options: duplication or sharing.
Shared Kernel model
Using the sharing model, you create components used by multiple bounded contexts. When the sharing kernel gets modified, it affects all bounded contexts that depend on it. Thus, it creates strong coupling.
There are a couple of things you can do to mitigate this downside:
Ā· Keep the shared kernel small, as small as you can
Ā· Ideally, only data structures that are shared between bounded contexts should be kept in the shared kernel
Ā· Continuous Integration ā Each time the shared kernel changes, the bounded contexts need to be updated as well and verified for conformity
Although this model creates coupling, it is still a valuable tool, especially when the duplication cost exceeds the integration cost. If you observe that the shared kernel changes frequently and you have to manage integration problems, maybe consider duplication.
Supplier-Consumer
There are a lot of situations where one bounded context provides data or services to another. We call the context that provides data/services a supplier or an upstream-bounded context. The one that depends on that data/services is called consumer or downstream bounded context. When this happens, one of the bounded contexts is usually more important from a business perspective.
If the supplier is more important (its team has more decision power), the team that owns it will dictate the integration contract. Itās a take-it-or-leave-it approach. The supplier-bounded context defines the integration contract and the consumers must adapt. In this scenario, you can choose the Conformist model or the ACL model.
Conformist
If the downstream team can accept and adapt to the upstream teamās model, their bounded context relationship is called conformist.
Conformist Model
You might be wondering why a team would relinquish control and autonomy to another? If the integration contract provided by the upstream team adheres to an industry-recognized standard or if it is good enough for the downstream team, then this might be a pragmatic choice. Ego is a dangerous thing in software development. You have to know when to let it go.
Anti-Corruption Layer (ACL)
When the downstream bounded context is a core sub-domain(vital to the business), the conformist approach does not work, even if the power scale is still inclined towards the upstream team.
In this case, the downstream team refuses to conform. It can not dictate the integration contract but decouple itself from it. It can translate the integration contract the supplier team created into something meaningful and valid for them. This layer of abstraction protects their bounded context from outside interference and allows their domain model to evolve and not be dependent on external factors.
Simply put, an anti-corruption layer (ACL) is just an intermediate standing between the external integration contract and the internal domain model of the consumer-bounded context. Its purpose is to protect the business logic from outside noise and keep things valid and consistent.
Open-Host Service (OHS)
This integration model addresses cases where the balance of power favors the consumers. The supplier decouples its implementation logic from its public API to better serve consumers. Itās the reverse of the ACL situation.
The supplierās public interface does not have to conform to its ubiquitous language. Instead, the integration contract is built to serve downstream bounded contexts best.
Open-Host Service model
It is common for upstream services to expose multiple versions of the integration contract. This allows consumers to choose the contract best suited to their needs and gradually migrate to newer versions.
Separate Ways
Sometimes, the cost of integrating bounded contexts exceeds the time it takes to duplicate components. If that is the case, duplication is a pragmatic choice. Duplicating components, even if they might be the same for multiple bounded contexts, is also feasible if effective communication between teams is not possible. Using this approach, teams can evolve their bounded context independently.
Separate Ways Model
Conclusion
I hope I convinced you there is no right or wrong integration model between bounded contexts. Each approach has pros and cons and is suited for a particular scenario.
Iām also aware that my presentation of these integration models is not a deep dive into this topic but a good starting point, especially if you are a new DDD practitioner.