0

In Domain-Driven Design, we try to separate concerns between the functional areas (bounded contexts), and minimize dependencies between the contexts. The same entity can have different internal representation in different contexts. But in the communication between contexts (e.g. exposed APIs and/or events), do we tailor the representation of entities to each data consumer, or rather do we use a common representation?

For example, take the well-known separation (as diagrammed by Martin Fowler) between the Sales and Support contexts. Both contexts need to be aware of Customer and Product. But in Support context, a Customer has a list of Tickets; while in Sales context, a Customer is assigned to a Territory. Quite possibly, the internal representations will be completely different in the two contexts. But for exposed APIs and events, do we have a single Customer model that includes both these features, or do we have multiple models per context.

1 Answers1

3

The point of having separate bounded contexts is to have the freedom to express an entity in a way that is suitable to that particular context without having to worry about the rest of the system. That is how you would primarily manage functional clarity and data sanctity within the bounded context. So it definitely makes sense to have the Customer represented differently across contexts.

You can think of separate bounded contexts as different Microservices. They are deployed and scaled separately and don't share data. Their APIs are separate, catering to their own context's functionality. They own the models and are responsible for maintaining data sanctity within their boundaries without worrying about the rest of the application.

Note that you often have to preserve the same identity across contexts when you have different models for a related concept. You accomplish this by publishing domain events from the first context that initialized the entity, for other contexts to populate themselves appropriately.

In Fowler's example, the sales context might be the first context where a customer is initialized. So the sales context would generate the identity. The support context then adds a record to itself by listening to the CustomerCreated domain event, for example, anticipating future support calls from the customer.

When you need to collate data from different bounded contexts, you typically write a separate query or reporting service that operates with straightforward queries outside the domain logic. Views/Queries that fetch data and organize it for presentation normally stay outside domain logic.

Subhash
  • 3,121
  • 1
  • 19
  • 25
  • 1
    An alternative approach to collating data from multiple bounded contexts is to consider a join (e.g. of sales and support data for some global customer analytic view) a bounded context of its own, in which case the join logic is the domain logic for that BC. – Levi Ramsey Jun 20 '21 at 18:08
  • Thanks Subash. I understand from what you wrote that the CustomerCreated event will have a Customer payload that the Sales context understands, since the Sales context raised the event. But the Support context needs to be able to understand this payload so that it can create the customer in its internal representation. So both Sales and Support need to agree on the nature of the CustomerCreate event. Now consider what happens when Support updates its customer representation due to (e.g.) case being closed. What is the payload of the CustomerUpdate event? The Sales representation or Support? – Jonathan Goldberg Jun 21 '21 at 14:39
  • Let's consider an update to the CustomerCreated event. First, Domain Events are specifically meant to convey cross-domain info - their structure tends to be independent of the actual structure stored in the sales context and is aimed to be perennial. – Subhash Jun 21 '21 at 16:42
  • Second, when event structures do change (as they invariably do), what you have to do in the receiving context (Support) depends on the nature of the change. Ideally, the change is forward-compatible, meaning that the support context will continue to consume the event without issues. In the rare case where it is not, you must consider upgrading the support context first and then the sales context to ensure you don't lose information. – Subhash Jun 21 '21 at 16:47
  • You can apply the same thought process to all Domain events across the customer lifecycle. Hope that makes sense. – Subhash Jun 21 '21 at 16:48
  • Thanks Subash for getting back to me. So what you are saying is that the event structure for say Customer is indeed independent of specific bounded contexts. So now we have a common model for Customer events, but discrete and different models for Customer in APIs exposed by the different bounded contexts. If we go this way, can we at least say that items within the different models should at least be consistent, so for instance customer name should be Customer.name in all the models (and not Customer.customerName in one place, Customer.name in another, etc.). – Jonathan Goldberg Jun 22 '21 at 06:59
  • Yes, there is no harm in trying to maintain consistency. As long as there is nothing that holds one context hostage when another context changes, you are fine. Usually, you end up maintaining this consistency because receiving contexts tend to use the structure published in the event by the owning context (sales, in your case). – Subhash Jun 22 '21 at 14:28
  • The subtle point is to use the ubiquitous language consistently across the team. Everyone should be consciously aware that a customer in the Sales context is **not** the same as a customer in the Support context. This is the reason it also makes sense to organize teams around bounded contexts - it avoids confusion. – Subhash Jun 22 '21 at 14:30