views:

65

answers:

3

I'm wondering what the recommended approach is for dealing with relational data with the IRepository pattern.

My database has the following tables with column names in parenthesis:

  • Plans (PlanId, Name, CreationDate, ModifiedDate, ViewId)
  • Areas (AreaId, Name, nTop, nLeft, nRight, nBottom)
  • Views (ViewId, nTop, nLeft, nRight, nBottom)
  • PlanAreas (PlanId, AreaId)

Where each plan can have zero or many Areas but only one view, so Plans.ViewId is FK to Views.ViewId. In PlanAreas, both columns are FK to the respective tables.

There are times when my application might want to act independently on the areas, but usually I will be loading, saving, deleting a plan and all of its constituents (areas, views) at the same time.

I've started down the path ....

public interface IPlanRepository
{
   IEnumerable<MyModel.Plan> GetAll();
   MyModel.Plan GetByName(string sName);
   MyModel.Plan GetById(string sId);
   void Delete(MyModel.Plan plan);
   void SaveOrUpdate(MyModel.Plan plan);
}

public class Plan
{
   public Guid Id { get; set; }
   public string Name { get; set; }
   public DateTime Creation { get; set; }
   public DateTime Modified { get; set; }
   public MyModel.View View { get; set; }
   public IList<MyModel.Area> Areas { get; set; }
}

public class View
{
   public Guid Id { get; set; }
   public IEnvelope Envelope { get; set; } // encapsulates top, left, bottom, right
}

// etc.

The plan is fairly complex, so there will actually be more properties, but this is a good start. So now for the questions:

  • Do I need IViewRepository and IAreaRepository?
  • When implementing the methods in IPlanRepository, do I do all of the work of grabbing the relational data tied to a plan (i.e. Areas, and View) and return a fully populated Plan object?
  • Or is it better to have a higher-level "aggregator" (for lack of a better word) that will fill in the properties once the Plan is returned? Something like this:

    Plan GetPlanById(string sId)
    {
        Plan myplan = new Plan();
        IPlanRepository planrepo = new PlanRepoImpl();
        myplan = planrepo.GetById(sId); 
        IViewRepository viewrepo = new ViewRepoImpl();
        myplan.View = viewrepo.GetByPlanId(sId);
        return myplan;
    }
    

Right now I'm planning on using LINQ-SQL for my data access because I'm familiar with it, and I can do it fairly quickly. I may switch to something else down the line, but I want to keep it simple for now.

A: 

You definitely do not need a repository for the sub parts of a plan. Since they are internal to the model you probably do not need to access them directly and if you did you would probably need a reference to the plan anyway.

Since that is the case you would want to use the Plan as your main point of reference. Using a full populated plan object you can then find out anything you want to know about the areas and views that are associated with it.

EDIT: I have been reading the DDD book by Eric Evans lately and for his repository he used a similar style to what I have described above.

smaclell
Basically the same answer as @bruno, but I've giving you the check for the book reference and because you have fewer points.
Keith G
+1  A: 

You should implement a single repository for each Aggregate Root. So, it seems to me that you only need one repository here. And yes, I think the repository should be responsible for building an Entity with all it's related data.

bruno conde
A: 

If you don't do a repo for the sub parts of the plan, how do you handle loading only the parts you need? If, for example, I wanted to get a list of all the plans in the system and choose one to view in detail you absolutely don't want to return fully populated plan object just to show a summary list. That would kill performance in so many ways. Instead you'd just want a lean plan object and maybe the count of how many areas it had (rather than all the fully populated areas). And, on top of it, you'd want to do all this in a reusable generic fashion.

I would lean toward the notion of separate repositories for each prime entity you wanted to work with. Then manage relationships between those through some sort of aggregator. Or, perhaps you pass a list of property references and associated delegates (or expression trees) to each retrieval method on your aggregate root repo to tell it which items to fill. But you still have the issue of 2nd level, third level, 4th level, etc. relationships. Back to the original idea of just needing to show a list of plans and a count of the areas...you wouldn't want to return all the area to just get a count.

And that's just retrieval. What if your Plan had 5 Areas and each Area had a series of Envelopes and you decided to add, edit, or delete an Envelope from an Area. How would you walk the tree to figure out what to update when you're at the Plan level? Seems it would make more sense to manipulate an Envelope through and Envelope repo.

It seems to me the repo handles data retrieval and another engine/class handles relationship fixup.

Fantus