Search code examples
entity-frameworkdomain-driven-designpersistenceddd-repositoriesddd-service

Entity Framework and DDD - Load required related data before passing entity to business layer


Let's say you have a domain object:

class ArgumentEntity
{
    public int Id { get; set; }
    public List<AnotherEntity> AnotherEntities { get; set; }
}

And you have ASP.NET Web API controller to deal with it:

[HttpPost("{id}")]
public IActionResult DoSomethingWithArgumentEntity(int id)
{
    ArgumentEntity entity = this.Repository.GetById(id);
    this.DomainService.DoDomething(entity);
    ...
}

It receives entity identifier, load entity by id and execute some business logic on it with domain service.

The problem: The problem here is with related data. ArgumentEntity has AnotherEntities collection that will be loaded by EF only if you explicitly ask to do so via Include/Load methods. DomainService is a part of business layer and should know nothing about persistence, related data and other EF concepts.

DoDomething service method expects to receive ArgumentEntity instance with loaded AnotherEntities collection. You would say - it's easy, just Include required data in Repository.GetById and load whole object with related collection.

Now lets come back from simplified example to reality of the large application:

  1. ArgumentEntity is much more complex. It contains multiple related collections and that related entities have their related data too.

  2. You have multiple methods of DomainService. Each method requires different combinations of related data to be loaded.

I could imagine possible solutions, but all of them are far from ideal:

  1. Always load the whole entity -> but it is inefficient and often impossible.

  2. Add several repository methods: GetByIdOnlyHeader, GetByIdWithAnotherEntities, GetByIdFullData to load specific data subsets in controller -> but controller become aware of which data to load and pass to each service method.

  3. Add several repository methods: GetByIdOnlyHeader, GetByIdWithAnotherEntities, GetByIdFullData to load specific data subsets in each service method -> it is inefficient, sql query for each service method call. What if you call 10 service methods for one controller action?

  4. Each domain method call repository method to load additional required data ( e.g: EnsureAnotherEntitiesLoaded) -> it is ugly because my business logic become aware of EF concept of related data.

The question: How would you solve the problem of loading required related data for the entity before passing it to business layer?


Solution

  • In your example I can see method DoSomethingWithArgumentEntity which obviously belongs to Application Layer. This method has call to Repository which belongs to Data Access Layer. I think this situation does not conform to classic Layered Architecture - you should not call DAL directly from Application Layer.

    So your code can be rewritten in another manner:

    [HttpPost("{id}")]
    public IActionResult DoSomethingWithArgumentEntity(int id)
    {
        this.DomainService.DoDomething(id);
        ...
    }
    

    In DomainService implementation you can read from repo whatever it needs for this specific operation. This avoids your troubles in Application Layer. In Business Layer you will have more freedom to implement reading: with serveral repository methods reads half-full entity, or with EnsureXXX methods, or something else. Knowledge about what you need to read for operation will be placed into operation's code and you don't need this knowledge in app-layer any more.

    Every time situation like this emerged it is a strong signal about your entity is not preperly designed. As krzys said the entity has not cohesive parts. In other words if you often need parts of an entity separately you should split this entity.