Search code examples
asp.net-coredomain-driven-designrepository-patternravendbdata-access-layer

How to handle DAL for multiple layers of 1 to many relationship?


I have the following problem: I have an aggregate root with multiple layers of one to many relationship.

Root -> has many
Child -> has many
GrandChild

I have Controller\s that handle logic done on each layer of the aggregate root.

I do not know how to handle the data access layer.

Should i just create a repository for the aggregate root and all Child and GrandChild operations get handled through it or it would be fine to create a repository for every level ?


Furthermore the GrandChildren entities in my case occupy a lot of space (they contain text) , thus i will be using a document database - RavenDB.

public class Root
{
  public int ID{get;set;}
  public IEnumerable<Child>Children;
}
public class Child
{
   public int ChildID{get;set;}
   public IEnumerable<Child>GrandChildren; //occupy a loot of space !
}
public class GrandChild
{
  public int GrandChildID{get;set;}
}

public interface IGenericRepository<T>
{
    bool Add<T>(T newValue);
    T Get<T>(int id);
    IEnumerable<T> GetAll();
    bool Delete(int id);   
    bool Update<T>(T value);
}

Controller

public class ParentController
{
    IGenericRepository<Root> repo;
    public IActionResult<Root> Get(int rootId)
    {
         return this.repo.Get(rootId);
    }
} 
public class ChildControiller_V1
{
    IGenericRepository<Child>repo;
    public IActionResult<Child> Get(int childid)
    {
          this.repo.Get(childid); //the id is unique 
    }
}

Access through root

 public class RootRepository:IGenericRepository<Root>
 {
     /// implementations
     public IGenericRepository<Child> GetChildRepository()
     {
            return //some implementation of IGenericRepository for Child
     }
 }
    public class ChildController_V2
    {
        IGenericRepository<Root>repo;
        public IActionResult<Child> Get(int rootId,int childid)
        {
              var root=this.repo.Get(rootId);
              var childRepo=root.GetChildRepository();
              var get= childRepo.Get(childId);

        }
    }

I hope you get the idea.For more layers i would do this all the way down.What would be a good approach considering the lowest entities occupy a lot of space compared to the others?

Update

The Root will have to support Create,Delete - not much happening here
The Child will have to support Create,Delete - (The focus will be on GET , something like GET 5 children starting from index=10
here)
The Grandchildren will have to support full CRUD with a heavy intensive work on Update .Their table size of GrandChildren will be >>>>> all the others combined.Every Grandchild will have a plain text column. When i say table or column i am referring to their equivalent in a typical SQ L database


Solution

  • From a (classic)DDD point of view, repositories return fully materialized aggregates, where aggregates represent consistency/transactional boundaries. Having children and grandchildren repositories means you give that up and with it a big benefit of DDD. That being said, you need to determine where your consistency boundaries are. Are there constraints between the entities? If not, they can be there own aggregates. Remember that aggregates don't appear in other aggregates and references from an entity should only go to an aggregate root, not some other entity in another aggregates' hierarchy.

    I already mentioned a few other points that may be interesting in my answer here, specifically a direction if the data is too much. https://stackoverflow.com/a/59189413/2613363

    Lastly, I will say Raven has versions (etags). Use them if you end up updating many child items.