DDD and references between aggregates in EFCore and C#
Asked Answered
K

2

6

I have an issue that I am not sure how to solve when DDD is assumed and using C#/EF Core.

Simplified situation: We have 2 aggregates - Item and Warehouse. Each of them has its identity by ExternalId(Guid) to identify it outside (FE etc) which is also treated as its domain identity. It also has database Id that identifies it inside databases model - Entity model and Db model is the same class as EF Core allows to use private fields - only the ExternalId, and required fields are exposed. Entity (both in DDD and EF Core sense) contain quite a lot of business logic and methods strictly coupled to the object. In general I follow the pattern from eShop/eShopOnContainers example.

Item is assigned to Warehouse and when creating an item we need to pass Warehouse to its constructor.

Is it proper to pass full Warehouse object to Item's constructor (but also to other methods that Item defines):

public Item(Warehouse warehouse,..)

or should I relay on database Id only :

public Item(long warehouseId,..)

I have an issue about this, because from one one side I read that aggregates should not reference other aggregates, but on the other hand using Datbase DB leaks the implementation detail (object persitsance in relational DB) to domain model which should not happen in my opinion.

Using ExternalId:

public Item(Guid warehouseId,..)

does not solve the problem as actual relations in db do not base on it.

What is your opinion ? I am a bit puzzled.

Kitchenette answered 2/3, 2019 at 22:29 Comment(1)
Sometimes having 2 sets of models comes in handy: the domain model and the persistent model :)Jacinto
G
1

Usually you would create a Value Object for the Id of the Aggregate Root. It is one possibility to rely on a Id generated by the database. If you decide to let the Db generate the Id, then you will need to work with that. But why would you need to pass the Warehouse reference or Id anyways? It looks like Item is an Entity and Warehouse is the Aggregate Root that should contain that Entity. In general you should not create an Entity outside of the Aggregate Root.

Edit: There are several identity creation strategies as Vaughn Vernon describes in the red book. One of them is let the persistance mechanism such as a SQL Db generate the unique identifier of an entity or aggregate.

Galenism answered 2/3, 2019 at 22:51 Comment(8)
The picture presented above is just simplified. They both need to be Aggregate roots. The item is ont required to have a Warehouse assigned.Kitchenette
@Kitchenette If they are really both aggregate roots, then you probably need the warehouseId only because you have somewhere overlapping in the "transaction boundries". The AR should be accessible only by its own Id and should definitly not rely on some other aggregateRootId in the constructor, because this would mean that you only can create an Item if there is a warehouse.Galenism
OK. Forget the presented example. Let's assume then that the Warehouse is not passed by constructor but by Item's method: AssignToWarehouse. Should the I pass Warehouse or WarehouseId?Kitchenette
AR reference other ARs always by Id.Galenism
Why ? Why ath are drawback of referencing them directly ?Kitchenette
Well the whole idea of an AR is that it fits a certain "consistency boundry" or "transacion boundry". By passing the object reference you run at risk to merge Item and Warehouse transaction boundries. Also you expose the API of the Warehouse to the Item directly, which makes it hard to reason about the code (can I call this method of the warehouse or not) and finally you load an unneccessarily large object tree everytime you want to execute a command on Item.Galenism
Ok. Almost convinced but passing just an id leaks the Database Id which should be transparent to domain.Kitchenette
Is your question answered? Or do you still see some problems?Galenism
C
0

Your domain model created during analysis is often different from the one created during design. Semantically they are both the same, you are passing references, but the design model recognises that you have to persist the data so you might not want to pre-load all referenced objects for performance reasons, whether that is simply loading it from disk within the same domain, or from a remote service in another domain.

Cozy answered 21/6, 2020 at 10:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.