Complexity finds us again
In the intricate landscape of modern enterprise systems, the challenge of integrating diverse applications and data sources is a constant. Often, these systems evolve independently, leading to a complex web of interactions that can be difficult to manage and maintain. We can address the technical challenges of communication with proper integration patterns, good practices and architectural styles. Unfortunately there are aspects of communication, like understanding data, business behaviour and language in various contexts, or mapping out dependencies, that also need to be addressed. This is where the purely technical approach fails.
Bridging Bounded Contexts
To navigate this complexity effectively, we can turn to Domain-Driven Design (DDD), which offers a powerful concept: "Bounded Contexts". They represent distinct areas of a business domain, each with its own unique model and "ubiquitous language" . This means that the meaning of a particular term or concept can vary depending on the specific bounded context in which it's used. This in turn creates a need for maintaining clear boundaries between these contexts as a crucial part of managing complexity and ensuring that each part of the (eco)system remains coherent.
However, the existence of these distinct bounded contexts naturally leads to a need for integration. Business processes often span multiple contexts, requiring data and services to be shared and coordinated. This is where Enterprise Application Integration (EAI) comes into play. Here, we’d like to dive in and explore how DDD and EAI intersect to facilitate communication between bounded contexts.
As we dive into this article, we’re clashing two different jargons, ubiquitous languages of DDD and EAI. For a side by side comparison and definitions, see the Glossary section at the end of this article.
Understanding Bounded Contexts
The importance of bounded contexts lies in their ability to manage complexity. By clearly defining boundaries, we prevent the mixing and confusion of concepts from different parts of the business.
For example, a "customer" in the sales context might have different attributes and behaviors than a "customer" in the support context. Within the sales context, "customer" might refer to a potential buyer with information like lead source and purchase history, while in the support context, "customer" might refer to an existing user with details on service tickets and support interactions.
Clear boundaries
Maintaining these clear boundaries and distinct models within each context is crucial. It ensures that each part of the (eco)system remains coherent and focused on its specific purpose. The ubiquitous language within a bounded context is vital for effective communication among domain experts and developers. It fosters a shared understanding and reduces ambiguity. However, it's essential to recognize that the ubiquitous language can differ between contexts. What one term means in one context may have a different meaning or be entirely absent in another.
While bounded contexts provide clarity and manage complexity, they also necessitate mechanisms for integration. As business processes often span multiple contexts, requiring data and services to be shared and coordinated. This leads us to the need for effective integration strategies. Essentially, bounded contexts are like specialized departments within a company; they each have their area of expertise, but they must collaborate to achieve the company's overall goals.
The Customer-Supplier Relationship in EAI
In the context of Enterprise Application Integration (EAI) and bridging bounded contexts, the "customer-supplier" relationship is a fundamental pattern. While there are other types of relationships, like partnership, or shared kernel, they do not really exist on the ecosystem layer, as they occur on a smaller scale. That means they usually are present in systems, something designed and developed in a closer knit environment (e.g. dev teams of the same company, or even the same development team). The situation we face on an ecosystem level is that the systems are built not only by completely different teams, but quite often also by different companies (e.g. SaaS solutions, custom software made by consulting companies). Most of the time they do not necessarily share the exact same purpose. Think of service providers, they usually aim for more generic service, and it is only our business process that provides context and defines which options/parameters are used. If we’d analyze the relationships between various applications in the ecosystem architecture, we’d see that the customer-supplier relationship is omnipresent.
At the heart of this pattern lies the distinction between "upstream" (supplier) and "downstream" (customer) systems. The upstream system is the one that provides services, data, or functionality. Think of it as the source of truth or the system that owns a particular piece of information. That leaves the downstream system to be the one that consumes or utilizes these services or data. It relies on the upstream system to fulfill its needs.
Consumer-Supplier relationship in DDD
In EAI terminology, these concepts often translate to "provider" and "consumer." The provider system exposes an interface or API that other systems can use, while the consumer system integrates with that interface to obtain the necessary resources or use a specific service, so consumes the interface. This relationship isn't merely technical; it often reflects real-world business relationships and power dynamics. The upstream system, as the provider, may have more control over the data format and service availability. The downstream system, as the consumer, must adapt to the provider's offerings.
For example, consider an e-commerce platform that exposes an API for new orders. A fulfillment system—responsible for processing and delivering those orders—integrates with this API to retrieve order data and initiate downstream logistics processes. In this case, the e-commerce platform acts as the upstream system (supplier), owning the order data and defining the integration contract. The fulfillment system is the downstream system (customer), consuming that contract.
But is that all that can be said about the customer-supplier relationship? Certainly not! Within this there are distinct behaviours that have further implications on how systems interoperate.
The Conformist Relationship
In the case of integrating bounded contexts, the "Conformist" relationship represents a specific approach to how a downstream system interacts with an upstream system. In this pattern, the downstream system chooses to align its own internal model with that of the upstream system. Essentially, the downstream system conforms to the data structures, service interfaces, and behaviour provided by the upstream system without significant transformation or adaptation.
Conformist relationship in DDD
This means that the downstream system directly uses the data and services as they are offered by the upstream system. If the upstream system provides data in a specific format or through a particular API, the downstream system adopts that format and API without introducing an intermediary layer to translate or modify it. This approach can significantly reduce the development effort required for the downstream system, as it eliminates the need for complex mapping or transformation logic.
However, the Conformist relationship comes with certain implications. By conforming to the upstream system's model, the downstream system becomes tightly coupled to it. Any changes to the upstream system's data structures or APIs may require corresponding changes in the downstream system. This introduces significant risks in highly volatile (eco)systems and, as the conformist approach offers close to non protective mechanisms, can lead additional problems:
- High maintenance overhead - Each change upstream cascades downstream, causing continuous integration and testing efforts.
- Reduced autonomy - Downstream teams lose control over their internal domain model and become reactive, forced into constant adjustment to upstream changes.
- Increased risk of misalignment - Frequent changes increase chances for integration issues, misunderstandings of changed data semantics, or misaligned business rules.
Despite these potential drawbacks, the Conformist relationship can be a suitable choice in certain scenarios. For example, when integrating with a well-established industry standard API or a legacy system that is unlikely to change, conforming to its model may be the most practical and efficient approach. It can also be beneficial when the downstream system has limited resources or a tight deadline, as it allows for faster integration.
Open-Host Service (OHS)
As we mention an industry standard, this is a good chance to introduce the "Open-Host Service" (OHS) relationship, which is another crucial concept in integrating bounded contexts. In this relationship, the upstream system (provider) creates a stable, well-defined interface, often referred to as a "published language," that acts as a facade. This interface shields the internal implementation details of the upstream system, providing a consistent and predictable, so standardized, way for downstream systems to interact with it.
Open-Host Service relationship in DDD
Essentially, an OHS acts as an abstraction layer, offering a standardized set of services and data formats, abstracting the internal structure and logic. This allows the upstream system to evolve without disrupting downstream systems that rely on the OHS interface. By loosening the coupling between the internal workings and the external interface, the OHS promotes a solution that is more resilient and extendable, making integrations more reusable and maintainable.
In the context of application integration, the OHS provides a standardized interface, typically through clearly defined APIs representing publicly available data and services. By establishing a clear contract, OHS defines exactly which services are available and how to access them. This stability and predictability facilitate collaboration and integration across different teams and systems. Although it is worth noting that sometimes the OHS is enforced upon systems by compliance with specific standards well established in an industry, standardization like ISO (e.g. ISO 20022) or laws and regulations (e.g. OpenBanking or EU PSD2 regulation with its respective country implementations).
The key benefit of OHS is that it allows the upstream system (provider) to evolve its internal model without breaking downstream integrations. As long as the OHS interface remains stable, downstream systems can continue to function even if the upstream system undergoes significant changes. This provides flexibility and agility, allowing the upstream system to adapt to changing business needs without causing disruption.
Anti-Corruption Layer (ACL)
Shifting the perspective to the consumer side, the "Anti-Corruption Layer" (ACL) relationship addresses the challenges that arise when a downstream system (consumer) needs to integrate with an upstream system whose model or data format is significantly different from its own. In this relationship, the downstream system creates an abstraction layer that is responsible for translating the upstream system's model into its own internal model.
Anti-corruption Layer in DDD
The primary purpose of an ACL is to protect the downstream system from being "corrupted" by the upstream system's data or model. It acts as a buffer, insulating the downstream system from changes or inconsistencies in the upstream system. By creating a translation layer, the ACL ensures that the downstream system only deals with data and services that align with its own internal logic and structure.
In the context of application integration, ACL enables looser coupling between the communication and the logic of the downstream system. It allows the downstream system to maintain its own domain model and business logic, even when integrating with external systems that use very different data models and formats. The downstream system becomes more resilient to changes in the upstream system. This means that the impact of frequent, often breaking, changes done to the upstream system, will be limited to the ACL.
The benefits of ACL include increased autonomy, reduced coupling, and insulation from upstream changes. By creating a translation layer, the downstream system can protect its own integrity and maintain its own internal consistency. This further contributes to the downstream system evolvability. Additionally the ACL reduces the risk of unintended consequences when integrating with external systems.
Having OHS and ACL
In many complex integration scenarios, both Open-Host Service (OHS) and Anti-Corruption Layer (ACL) relationships are used in conjunction. The upstream system can provide an OHS, offering a stable and well-defined interface, while the downstream system can use an ACL to further adapt the data to its specific needs.
This combination provides a flexible integration, balancing the needs of both providers and consumers. The OHS ensures that the upstream system can evolve without breaking downstream integrations, while the ACL ensures that the downstream system can maintain its own internal consistency and autonomy.
For example, consider a legacy system that provides data through an OHS. A modern application that needs to integrate with this legacy system can use an ACL to translate the data from the OHS into its own domain model. This allows the modern application to work with the data in a way that makes sense for its own business logic, without being constrained by the legacy system's data structures.
In complex integration scenarios, especially those involving integration platforms, using both, or even multiple, OHS and ACL can significantly improve the overall architecture and maintainability of the (eco)system. It allows for a clear separation of concerns, promotes loose coupling, and provides flexibility for both providers and consumers.
Glossary
As you have probably noticed by now, the language used in Domain-Driven Design differs from the one used by us to describe application integration. To make life a little easier here’s a cheat sheet of terms in both jargons, or at least their closest related terms:
Conclusions
Facing complexities of modern integration between various applications like SaaS solutions, custom made software and legacy systems, it is important to build an understanding of the common pitfalls that we can encounter. The Open-Host Service (OHS) and Anti-Corruption Layer (ACL) can be crucial tools for building flexible and maintainable architectures.
The combination of OHS and ACL is particularly powerful, especially in complex scenarios. By leveraging OHS, providers offer a clear and stable point of interaction, while ACL allows consumers to adapt and integrate data without compromising their own integrity. This approach promotes resilient ecosystem interactions, enabling systems to evolve independently while still effectively communicating and sharing data. In essence, OHS and ACL are not merely technical patterns but architectural strategies that facilitate collaboration, reduce dependencies, and ensure the long-term viability of integrated systems.