0

I can't really find a satisfying solution for that problem. I have a n-layers application :

  1. UI
  2. Presentation (Domain Model used as DTO, ViewModels are then presented)
  3. Business Layer (Domain Model)
  4. Repository and DAL (Data Model)

My problem is that I need to work with full objects in my business layer. However I can't figure out what is the best way to load them. The question may look stupid - and perhaps it is - but I'm very puzzled by it. I have the following classes (just to illustrate):

public class Library
{
    int ID {get;set;}
    string Name {get;set;}
    Book[] Novels {get;set;}
    Book[] TextBooks {get;set;}
}

public class Book
{
    int ID {get;set;}
    Library[] SalePoints {get;set;}
    string Name {get;set;}
    string Type {get;set;}
}

And I have two Service Methods LoadLibrary and LoadBook whose aim is to populate these respective models. My problem is that it looks like I will get into an infinite loop. I'll explain myself right now.

I have pre-loaded domain models coming from the Repositories (The repositories convert the Data Models into domain models using valueinjecter (unflatenning), so basically the simple fields (int, string, etc..), are populated in the resulting domain model but the IEnumerable properties are not. That's why I'm finishing to load them in the Service Layer. (There are also two reaons why I can't fully load them in my repositories : 1/ I'm using a generic repository and 2/ The loading logic relay on business rules)

Let us say that I do the following :

public class LibrairiesProcessor
{
    public Library LoadLibrairyObject(int ID)
    {
        Library l = UnitOfWork.LibraryRepo.GetByID(ID); // Only retrieve "simple" properties, here it will get the Name and the ID properties;

        l.Novels = UnitOfWork.BookRepo.Get(b=>b.LibrairyID==l.ID && b.Type="Novel");
        l.TextBooks= UnitOfWork.BookRepo.Get(b=>b.LibrairyID==l.ID && b.Type="TextBook");


        for(int i=0;i<l.Novels.Count();i++)
        {
            l.Novels[i] = BooksProcessor.LoadBookObject(l.Novels[i].ID);
        }
    }
}

and

public class BooksProcessor
{
    public Book LoadBookObject(int ID)
    {
        Book b = UnitOfWork.LibraryRepo.GetByID(ID); // Only retrieve "simple" properties, here it will get the Name and the ID properties;


        b.SalesPoint = // Get From repo using the Unit of work

        for(int i=0;i<b.SalesPoint.Count();i++)
        {
            b.SalesPoint[i] = LibrairyProcessor.LoadLibraryObject(b.SalesPoint[i].ID);
        }

    }
}

Isn't there an infinite Loop ? LoadLibraryObject() calling LoadBookObject() and vice versa... And especially when you come to the book whose SalePoint is the librairy who triggered LoadBookObject()... at best the same work is done twice, but I really suspect that it will never end.

So I'm wondering if I'm doing the things right. My goal is to have fully loaded objects to avoid wondering if the object is enough loaded before using it, but I'm not sure of the solution I've came up with. How do you usually achieve that ?

It looks like a trivial issue but seriously I can't figure out how to load my stuff. I feel like I have a dilemma :

1/ Manually Populating all the properties and sub properties by using UnitOfWork.Repository, but I will have a lot of code and a lot of redundant code.

2/ Live with partially loaded domain models with some properties set to null, but I really don't want that. And since I'm not working with EF objects directly since my domain model is complex, I can't use their lazy loading stuff.

3/Call the Service LoadObject Methods from one LoadObjectMethod to an other, to avoid code redundancy, but it looks like I'm introducing a lot of recursivity.

How should I handle that ?

Thanks !

tobiak777
  • 3,175
  • 1
  • 32
  • 44

1 Answers1

2

This is probably best suited for CodeReview, but here are a few thoughts that may save you some trouble :

  • Having a whole separate DAL object model might be overkill. Most modern ORMs will take care of the domain objects/DB mapping for you without polluting your domain model with persistence stuff. See Should I create in BLL project class the same like poco class in DAL project and return it to UI project if I want to display data from database?

  • Having a generic repository seems like a poor excuse for not being able to load lists of sub-entities. As for "the loading logic relies on business rules", if you're referring to the Novels/TextBooks distinction, there are other, less class-intensive ways of doing this.

  • "since I'm not working with EF objects directly since my domain model is complex, I can't use their lazy loading stuff" : how exactly is your model complex ? Technically speaking, why couldn't you use lazy loading ?

  • As a good practice, Aggregates shouldn't refer to one another with navigatable properties but with (lists of) ids. The object that got an Aggregate root from a Repository will call another Repository if it wants to load associated aggregate roots.

  • UnitOfWork.LibraryRepo looks awkward. The UoW should be injected or directly instantiated in the method, not made available as a singleton. Same is true for the Repository. Things will be more testable and loosely coupled if you pass an ILibraryRepo around to the method instead. Unit of Work and Repository are two separate concepts, they somehow converge in Entity Framework but from the outside, they shouldn't look as tightly tied togteher as that.

Community
  • 1
  • 1
guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • Thank you very much for your help guillaume. Actually after searching I read that using domain object in the DAL was bad practice. I actually didn't know that EF could do the job without messing up my domain objects. Concerning the loading of subentities, it will definitely be easy if I'm fully using EF. I actually believe that in my case separate DAL Object Model is Overkill, it doesn't bring anything to the Table. When I said that my domain is complex, I actually meant that my domain models are not POCOs. Perhaps I need to have closer look at EF, I might be underestimating its features. – tobiak777 Jun 19 '14 at 19:52
  • But then, if Aggregates only holds roots of ID, you mean that my properties in my model have to look like : ICollection AggregateRoot2 {get; set;} ? But by doing so, it looks like I'm loosing the convenience of OOP isn't it ? are you saying that my domain objects have to be POCOs having sometimes ListofIDs and navigable properties for simple Aggregates ? Thanks, actually I'm instantiating the UOW in my service class! I'll go ahead and post this question in Code Review ; ) – tobiak777 Jun 19 '14 at 19:58
  • This isn't entirely on-topic on Code Review. We only review working code. Please refer to [this post](http://meta.stackoverflow.com/questions/253975/be-careful-when-recommending-code-review-to-askers) before recommending Code Review again. – Jamal Jun 19 '14 at 20:20
  • 1
    @Joey An aggregate is a consistency boundary, you should avoid modifying multiple Aggregates in the same business transaction if you can. Thus the need for navigation properties from AR to AR should be limited. Also, referencing ARs by their id improves storage scalability. You'll find out all about that in [Vaughn Vernon's Effective Aggregate Design part 2](http://dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_2.pdf) (page 8) or in [his book](https://vaughnvernon.co/?page_id=168). – guillaume31 Jun 20 '14 at 07:21
  • 1
    @Joey I don't think OOP necessarily implies loading a complete graph of objects that is traversable from end to end in one go. OOP is about message passing. You can perfectly have orchestrator objects that obtain a reference to a small graph by passing a message to a Repository, and later get another reference to a related graph from another Repo if needed. Also, the [Law of Demeter](http://en.wikipedia.org/wiki/Law_Of_Demeter) warns us against going too deep into nested objects. This is especially true IMO if you try to reach so deep that you cross conceptual barriers like Aggregates. – guillaume31 Jun 20 '14 at 07:35
  • @guillaume31 Thank you very much, last night I've also read the first two parts of Eric Evans' Domain Driven Design : Tackling Complexity in the Heart of Software (2003), and your answer made much more sense to me. Your last comments perfectly answer my last questions. Looks I have some ultimate changes to do ! – tobiak777 Jun 20 '14 at 08:38
  • No problem. It should make much more sense indeed when you've read about DDD's essential concepts :) Aggregate is one of them (if not the most essential), unfortunately it's not easy to grasp. – guillaume31 Jun 20 '14 at 08:52