3

When a single physical entity means different things in different contexts, DDD principles recommend creating different entities in each bounded context.

This is an example taken from the book "Patterns, principles and practices of Domain-Driven Design": A "Product" is a concept that must be acquired with a profitable margin and an acceptable lead time to the Procurement team. Yet to the Sales team a product is a concept with images, size, guides, and belongs to a selling category—none of which are relevant to the Procurement team, even though it is the same physical entity in the problem domain.

My question is, how does the Sales bounded context and the Procurement bounded context know they are dealing with the same product? How do you find the same entity in the Sales context and Procurement context for reporting purposes?

How do you store the identity in the database? I expect each Product class to have it's own table. Normally identity is autogenerated by the database. We can't guarantee Sales database and Procurement database would generate the same Id for the same physical entity. How do you link these two concepts in software?

3
  • 1
    "My question is ..." - the whole point of BCs is that building it all as one big application would result in a complicated mess. Instead, after your analysis of the way the business operates, you decided to build what almost looks like two separate, but simpler applications (each with its own database). Each deals with its own little bubble within the problem domain. Two representations of the same concept are matched through an identifier of some sort. This often involves eventual consistency, meaning, the business doesn't need the data of the two "applications" to be always in sync. 1/2 Commented May 26 at 19:42
  • 1
    This is super common. Most reports don't need real time data, and for some reports it makes no difference if the data is a day stale, or a week, or more. In other words, they'd be fine with some background process, or a even a third context (Reporting BC), that would collect data from various sources at a certain cadence, and store it in its own DB. In fact, there are tools designed precisely for that sort of thing, and the business might already be doing it. Part of DDD is to find such things out by asking questions, and see if they want you to integrate with systems already in place. 2/2 Commented May 26 at 19:42
  • 1
    P.S. I've managed to find a copy - what I'm wrote is essentially what the book is talking about a few pages down from your quote, under "Your Domain Model Is not Your Enterprise Model". The book expands on this in Ch7 (Context Mapping) and in Part II (Strategic Patterns). P.P.S. Note also that you can't divide your application into bounded contexts arbitrarily - the idea is to exploit or build upon the "loose coupling" already present in the real-world business, and for this you (your team) have to actively build up your understanding of the business problem your application is meant to solve. Commented May 26 at 20:50

5 Answers 5

3

From what you wrote, I guess you already on the right track: one needs to give products a unique ID as a common attribute, which is transferred from one bounded context to another.

Now there a couple of options how to implement this approach. Which one to choose depends mainly how the "bounded contexts" interact with each other and how your system architecture looks like.

Typically, there is one specific bounded context where Products arrive first in the system, maybe the Procurement context, maybe the Sales context, maybe a third one. There, the unique ID ist generated intially, maybe the product gets also a name or other basic attributes. Sooner or later, the other bounded contexts will be informed about the newly arrived products, either synchronously or asynchronously within certain time intervals, and there will be a synchronization process which transfers the information about the new products to any other context which needs to know about them - including the unique ID.

How this sync process looks in detail depends heavily on your specific system. I have seen systems using events to get other subsystems up-to-date within miliseconds. I have seens systems which run a nightly synchronisation routine to update subsystems within 24 hours. And I have also seen systems where an operator had to enter information from one system into another completely manually (probably together with a lot of other subsystem specific information which cannot be created automatically).

Normally identity is autogenerated by the database.

That may be normal when you have a system which contains only one, single database and only one Product table, but not in the kind of system scetched beforehand. When one bounded context creates the identity for certain records, so the identity information is transferred to other bounded contexts, only the first bounded context can use autogeneration, but not the depending subsystems. An alternative implementation could distinguish between autogenerated surrogate keys (which only provide "local identity" in each subsystem) and "domain keys" (which provide identity across different bounded contexts). This might look a little bit wasteful at a first glance, but today memory is cheap and since all these keys are immutable values, there is normally no issue with redundancy.

9
  • 2
    This is obvious from technical stand point. I'm not familiar with DDD, but does not it dictate that storage and technical concerns should not pollute the domain? How is that ID hidden from the clients and model implementation?
    – Basilevs
    Commented May 25 at 17:09
  • 1
    @Basilevs: who says it must be a technical key? The product id could be seen as a domain key, very visible for every stakeholder.
    – Doc Brown
    Commented May 25 at 19:20
  • @Basilevs The system the products first arrive in should be determined by looking at the business. Who decides when a new product should be created? Does that come from sales identifying a market gap, procurement identifying something that can be produced/acquired, or someone separate from either team? Answering that should tell you which system should be responsible in the domain for the initial creation of the product, and then again, look at how those buisness people communicate what they are talking about. There may be a catalogue number or an item name or something else. Commented May 25 at 22:54
  • @user1937198 sometimes the only identity is physical. Like a paper ticket without a number. When replacing these with IT an system, a generated ID is a technical measure to model something with an identity, but without identifier.
    – Basilevs
    Commented May 25 at 23:03
  • @Basilevs If the only identity is physical, how can multiple people identify it at once? It must have some property that identifies it: maybe location, or it must be held by a single context (who ever holds the item). Commented May 26 at 0:10
2

Indeed, if something has a different meaning in different bounded contexts, we'd end up with distinct entities in different bounded contexts.

There are however several ways to organise the mapping between the contexts. For example:

  • Related elements can perfectly be managed in both contexts in a joint manner (shared kernel). If it appears that two entities are in reality one and the same, it is perfectly possible to share the entity between the two models.

  • Elements can be managed independently in each context, but a relationship between the two established (directly, or using an anticorruption layer). Entities would then be distinct and related one-to-one. Each should then have its own identity, but it is possible in practice to use the same identifier, especially if the item in one of the context is always created before the dependent one in the other context. Example: if a purchased product would always created after the sold product because buying would first require the specification of what is to be sold.

However, I'd call for extreme caution when establishing such cross-boundaries mappings. Indeed, in my experience, the one-to-one is rather exceptional. High tech products often have one sales product definition (described in the marketing catalogue), but in reality correspond to different products manufactured or bought (slightly different configuration while the key product attributes remain the same). This is also the case for cars, where the commercial car model corresponds to several vehicle descriptors in the VIN). I also have worked in some areas that are the opposite, i.e. the same bought product is sold under two references (e.g. the same appliance repair component might be sold with two different references, each corresponding to a different appliance brand). Premature merging of entities is like premature optimisation, the root of all evil.

2

Before we get to DDD, if we consider simple data normalization, you already get to a point where you start identifying the needs for a shared root identity: the product.

On the premise that not every product has a 1-1 relationship with every department, it already leads to a three table solution:

enter image description here

In simple database terms: you create a product, which in turn generates an identity (PK) for that product, and then both Sales and Procurement can reference (FK) that product (PK). The product table contains all product-specific (shared) information, the other two tables contain the information that is relevant only to that department.

It is possible that your application does not reveal the product on its own. You could, if you were so inclined, create the UX in a way that the Sales and Procurement teams on their own pages are given the option to reference an existing product or trigger a new product to be created.
The question on what data you would want to store on the product to help identify them and assist teams in finding existing products is an exercise left to the reader.

In DDD terms, this creates a third bounded context: the product. As I mentioned before, whether or not the product is exposed as a separate bounded context to the outside world is a separate consideration. I would personally default to creating a dedicated Product CRUD API, but this can be avoided if there are valid justifications for doing so.

Sidenote:
In a world where you hide the product creation into the Sales/Procurement bounded contexts, I would prepare for scenarios where both departments ended up creating different identities for what they (retroactively) understand should have been the same product, and provide some kind of tooling to help identify and merge these scenarios.


We can't guarantee Sales database and Procurement database would generate the same Id for the same physical entity.

You shouldn't at the same time acknowledge that a product is the same identity and then plan to store it in a distributed fashion across two otherwise unrelated databases. For the same reason that you are splitting the sales and procurement data store, the product should be upheld by its own data stores.

If your concern is that of a hard FK constraint in the database, that is something you have to let go of when you separate your data stores (pretty much by definition of stores being separate). In a microservice/separate bounded context world, FK "verification" (i.e. checking referential integrity) should be done service-to-service, not table-to-table.

-1

Where do you draw the boundaries of these "bounded contexts"?

I would reckon that the vast majority of businesses do not consider their sales and purchasing contexts in isolation when it comes to the products they handle.

Businesses that I'm familiar with work from a common catalog of their stock-in-trade, otherwise business activity would be extremely difficult to account for or control financially, not to mention difficult for anyone to understand how to translate purchases into sales if they are not both working from a common catalog.

Again in businesses that I'm familiar with, the distinction between the purchasing and sales functions exists not because they are handling different kinds of stock, but because they are handling different external trading parties with whom the business has different relationships (that are managed in different ways) and with whom stock flows in different directions.

As well, keeping purchasing and sales staff separate reduces the overall visibility of prices and margins, and makes it somewhat more difficult for staff on either side of the divide to understand the whole network of trading relationships that supports the business (and is typically a trade secret).

The bounded context of a business is often the whole business, because although there may be specialist functions in larger businesses that can be considered locally and severally from the others, there are also many common concerns in any business, and the definition of what kind of stock is being handled is definitely a common concern.

-3

If you have the same thing in two bounded contexts, then it is persisted as a single entity in the datalayer. You are just using that data to populate different classes in the code.

Does this work in practice? Sure, as long as both products have the same properties. and its just the methods you are adding it's fine, if awkward.

Would it work in their example, where one product has a whole bunch of extra properties which are themselves child classes? I can see it becoming difficult fast.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.