Creating useful software models that represent the buisness they serve is hard. Creating a single model for a business at a point in time might be possible but as the business changes models need to be continually updated. Because these models are hard to produce and require a lot of analysis keeping them up to date becomes an impossibility. One model just can’t keep up with business reality.
Domain Driven Design (DDD) offers an alternate approach. By decomposing systems around bounded contexts and domains the models we produce are not only simpler because the capture less of the overall business domain but they are also optimized for a particular context. Each domain maintains its own models. Changes to a domain should have little or no impact on another domain.
It is very rare for any system to operate in isolation. Systems need to work with other systems using different models and technologies to deliver functionality to client applications and users. Email is ubiquitous, almost any consumer system probably needs to send emails from time to time.
Users of an online sales system need to maintain their contact details. Lets assume that each time a change is made a confirmation email is sent to the customer. A typical use-case. Email systems typically use a templating system. To send an email the Sales system needs to send meta-data to choose the right template and a key/value pair appropriate to the template. It is unlikely that any business oriented domain model would have data as key/value pairs.
Architecturally we need something that manages the exchange of information between the domain model and the other systems or domains they need to interact with. This something is typically referred to as the anti-corruption layer. The layer might be implemented as a facade or adapter to the other systems. I prefer the gateway name and pattern.
So at a high level we have a layer (gateway) that maintains a clean separation between our domains and services and capabilities that they require. What does this mean at lower levels of interactions?
Most systems use unique identities for entities that they manage. Traditionally these identities are database generated to guarantee uniqueness, and are numeric. Each system likes to maintain its own identities and the related entity lifecyle. Domain Driven models are the same. Entities, Aggregates and Aggregate roots use unique identities so that data can be referenced and found.
Often these identities are stored within the data model as external references. Adding these to the domain model corrupts the original model. If a domain has many integrations then multiple external identities need to be stored as references.
Assuming that these identities represent the 'same' objects a responsibility of the gateway should be to maintain a mapping of identities. Domain services use their own identities to initiate requests to external systems. Inbound data with external identities are mapped by the gateway.
Lets assume that the sales system needs to update the CRM system.
Maintaining a mapping requires persistence. This means that implementing a gateway is more complex than a simple facade.
Identities are typically unique only with the system that generated them. To understand an identity we need to know the system that generated it (realm) and its type (customer, order etc). Relational and graph databases make good stores for mapping identities.
The use of statistically unique values has changed the way we generate identities. In the past monotonically increasing index keys generated by a database were the norm. Computing a unique identity within the application has a number of advantages.
In our example lets assume that we need CRM changes to be reflected within the domain model. A new customer entry in the CRM system means that we need to create a new entity in the domain. The gateway maintains the mapping so needs to understand the relationship between the CRM identity and the new domain entity. This could be done with a callback or some other hook into the domains methods. Another option is for the gateway to assign the identity (uuid) and provide that as a request to the domain: 'New Customer to be known by <uuid>'. Traditional systems would still require correlating responses with requests or to receive the identity as a return value.
Precision, type, composition/decomposition are conversion activities. Systems have developed interesting formats. Key-Value, 'compound values. Older systems assume timezones for time and data. More recent domain implementations typically use typed fields. When ingesting two fields for date (DD/MM/YY, MM/DD/YY or YYYY/MM/DD) and time (HH:MM:SS) the gateway can enrich the data based on it’s knowledge of the system supplying the data 'Tue Jun 6 18:18:12 MDT 2017'.
Data mapping applies a look-up transformations to data values. Enum values and other defined lists are unlikely to match between systems. The Gateway provides a bi-directional transformation between systems.
Preserving domain integrity reduces the domain code making the domain more maintainable. Business logic changes centered in the domain code is isolated from integration concerns. External systems are easier to replace by writing a new adapter for the new system. The gateway becomes a 'choke point' for information flow that can be monitored/controlled.
Many other daily maintenance activities like API changes are isolated to the gateway. Interaction are explicit and easier to understand/maintain and update.
Creating gateways that isolate integration concerns maintains a clear set of responsibilities and helps defend domain models.