DDD - How to manage entities across bounded contexts in EF Core
Asked Answered
T

2

8

I have not found concrete examples of this. I know that each bounded context has its own versions of entities and you should not share entities across contexts. But how do I manage this in relation to using an ORM like EF?

For example, below are my entities and bounded contexts they exist in:

Ingredient (entity bounded context A)

Recipe (entity bounded context b)
Ingredient (entity bounded context b)
MenuItem (aggregate bounded context b)

Now each bounded context will have its own version of ingredient. But since I have a singular DB context in EF for managing this, how exactly do I arrange this? I am using CQRS so I can trigger events if needed. My plan was to maintain a list of ids in my Recipe entity and pull in the relevant Ingredients from the database so that data is not duplicated.

But I'm not sure if my data duplication concern is valid. In my example above, imagine a business which sells ingredients but also has preset recipes (with a list of ingredients) that it can sell in a food stall.

In one context, ingredients has no relation to another entity while in another context its a child entity (in an aggregate). I can see how it should be designed (separate entities in bounded contexts) but when it comes to the DB, how is this actually setup? What if the properties/domain knowledge that need to be tracked in context A vs context B for ingredients is different? Will this end up being a separate table? I'm a bit lost on this.

Edit: Please keep in mind I'm only using 1 database here. I understand that usually you have separate DB per bounded context to avoid this scenario but was wondering how this can be accomplished via 1 DB.

Tripod answered 1/3, 2020 at 22:4 Comment(0)
T
4

I would separate your question in two parts:

  1. An architectural part: Bounded context design/boundaries
  2. A technological part: Database schema management, EF mapping, etc

From point 1, I would stress that the Bounded Context boundaries are thicker than what you imply in your question. If boundaries are well defined, there is little data duplication between BCs, a part from some Ids and little more. In your case, the ingredients in BC-A have the same Ids than the ingredients in BC-B.

For example, your BC-A might manage the name, description, pictures and so on from the ingredients. BC-B does not need this information, but instead, it might have some important properties required in order to create the recipes. In a similar way, a BC-C might have the providers, prices and so on of every ingredient.

Also, in your BC-B, you might have the ingredients in two places: A table with all the ingredients and their interesting properties for the recipes and as Entities as part of a Recipe. This entity wouldn't have all those properties, instead it would have the Ingredient Id plus the quantity used in that recipe, for example.

With this type of set up, it's not only non desirable in principle, but even in practice, to share the Ingredients tables across Bounded Contexts.

Many times, the need from data duplication comes from UI needs. For example, you will need to show the Ingredient's names when you show Recipes, but this can be solved by UI composition. The UI can pull the recipes from BC-B and enrich the data with the names in BC-A and maybe with the prices in BC-C.

If you follow this approach, point 2 should fall on its own: every BC should have its own DbContext. It is OK to use the same connection string (database) for all DbContext, but you can initialize the DbContext with specific DB schema. This way, you can have an Ingredients table in each BC without DB table conflicts and you can have EF migrations versions tables per DbContext as well (although I believe it also works with a shared table). This set up also gives you the advantage of allowing you to separate the databases in a single DB per Bounded Context without having to change any code. You might need this eventually for scalability reasons, or to facilitate infrastructure as code, etc.

Tarpeia answered 2/3, 2020 at 19:6 Comment(9)
Thanks for your answer. I believe this is the approach I am going to take. Separate DbContext per BC. One question I had is if sharing a table for 2 different BC is a bad idea. I will be the sole dev in this particular case. I know in general data duplication is not bad in DDD due to the problem it is trying to solve. Instead of an ingredient table A in BC A and ingredient table B in BC B, can I end up sharing the same table if I only need access to quantity/name/id in BC B? Or is it a better idea overall to completely separate the tables entirely even in this case?Tripod
Comment got too long but I believe one caveat is that only BC should be responsible for creates/updates while all other BC have read only access (i.e. in the case where 2 or more BC share a table) - I do understand this is not generally recommended but in cases where it fits - is this a valid approach?Tripod
I'm not going to say that you should not do it, but it's definitely not recommended. Also, I would double check my boundaries if Quantity and Name where needed in two different BCs. It smells like bad boundaries to me, but it's impossible to tell without knowing more about your domain. It sounds correct that only one BC is in charge of creating Ingredients. It can then publish an IngredientCreatedEvent and the other BCs can then insert a record in their Ingredients tables. I would imagine that all BCs can update their Ingredients tables. BC-A can update the ingredient name, BC the price, etcTarpeia
In this particular instance, imagine BC A is a business selling ingredients in bulk and BC B is a sub business where premade recipes using available ingredients on hand are sold. BC B will need a list of ingredients from A (although they won't need all the properties and will only need price + name). In this particular instance I was wondering if I leave all ingredient creation to BC A - and BC B only pulls in the list from A and stores its "own" version i.e. price/name but uses the same table as BC A. Or I could store a table with just the properties I need in BC B (recipe_ingredients) .Tripod
OK. There are multiple ways you can implement this and it really depends on your requirements. I would certainly not share the table. I also don't think that the name needs to be in both BCs as it's not a property you need to do any business logic. Note that many times, we think that we need to share a lot of data, but in practice it boils down to a UI need. In one screen you can show data from multiple BCs. When displaying a recipe on the UI, you can get the recipe with IngredientIds and quantities from BC-B and the names and prices from BC-A. And if the bulk price is different from the priceTarpeia
within a recipe, then the price in BC-A is different from the price in BC-B. Then it's not data duplication, it's just every BC having its own data (even if it looks like the same thing).Tarpeia
Got it thank you. I think I was a little hung up on the properties being the same but in the end it depends on the BC needs. I'll only use domain specific knowledge properties only in BC A and if additional data is needed, I'll pull them in via the other BC for the presentation layer.Tripod
I understand that I can use domain events to notify changes from one BC to another BC. But how can you ensure that the change is consistent? I mean since I change data from first BC, it can fail in many ways, perhaps the notification doesn't arrive to the second BC. Or it arrives, but the is a delay time in which the information is not consitence.Roee
@ÁlvaroGarcía the answer to your question has a technological part and a design part. There are technical solutions to increase the reliability of the communication between BCs, for example, using out of process messaging, an Outbox and Inbox patterns, retries, implementing idempotent handling, etc. To handle delays between publish and handling, you have to look at the implications of enventual consistency. Maybe there was stock when the order was published, but it is gone when "shipping" handles it. Ask business how to handle these scenarios. The solution is not technical in this cases.Tarpeia
I
1

TL/DR: having a single db service is perfectly OK (for smaller services or due to some bussiness specific reason), so long as the data aren't shared. If you use a normal form db, use different tables to store your two ingredients.

Longer version:

If your ingredient entities in those two bounded contexts are backed by the same physical table, and moreover they can share data (one Ingredient entity can operate on data created by the other ingredient entity), then they're not really independent.

One very important aspect of having BC as a boundary, is to exactly allow those entities to evolve independently. By having them share a table (or document collection), you don't allow for that to happen. Moreover, this is violating the more generic (I mean non-DDD specific) guideline of "you shouldn't share a db across service boundaries".

Illusion answered 2/3, 2020 at 10:33 Comment(3)
I think I came to this conclusion as well trying to do a lot of digging around for this specific issue yesterday. I'm going to end up using the same DB but might go as far as using different schemas to separate concerns around BC.Tripod
@Tripod A different schema may not be enough; it should be different tables.Illusion
With above I just want to explain that if the schema used differs in some properties, but the table storing the ingredients is still the same, it's not enough.Illusion

© 2022 - 2024 — McMap. All rights reserved.