84

I currently have a repository for just about every table in the database and would like to further align myself with DDD by reducing them to aggregate roots only.

Let’s assume that I have the following tables, User and Phone. Each user might have one or more phones. Without the notion of aggregate root I might do something like this:

//assuming I have the userId in session for example and I want to update a phone number
List<Phone> phones = PhoneRepository.GetPhoneNumberByUserId(userId);
phones[0].Number = “911”;
PhoneRepository.Update(phones[0]);

The concept of aggregate roots is easier to understand on paper than in practice. I will never have phone numbers that do not belong to a User, so would it make sense to do away with the PhoneRepository and incorporate phone related methods into the UserRepository? Assuming the answer is yes, I’m going to rewrite the prior code sample.

Am I allowed to have a method on the UserRepository that returns phone numbers? Or should it always return a reference to a User, and then traverse the relationship through the User to get to the phone numbers:

List<Phone> phones = UserRepository.GetPhoneNumbers(userId);
// Or
User user = UserRepository.GetUserWithPhoneNumbers(userId); //this method will join to Phone

Regardless of which way I acquire the phones, assuming I modified one of them, how do I go about updating them? My limited understanding is that objects under the root should be updated through the root, which would steer me towards choice #1 below. Although this will work perfectly well with Entity Framework, this seems extremely un-descriptive, because reading the code I have no idea what I’m actually updating, even though Entity Framework is keeping tab on changed objects within the graph.

UserRepository.Update(user);
// Or
UserRepository.UpdatePhone(phone);

Lastly, assuming I have several lookup tables that are not really tied to anything, such as CountryCodes, ColorsCodes, SomethingElseCodes. I might use them to populate drop downs or for whatever other reason. Are these standalone repositories? Can they be combined into some sort of logical grouping/repository such as CodesRepository? Or is that against best practices.

e36M3
  • 5,494
  • 4
  • 33
  • 47
  • 2
    Indeed a very good question, that I've been struggling with myself a lot. Seems like one of those trade-off points where there is no "right" solution. While the answers available at the time I write this are good and covers most problems, I don't feel like they provide any "final" solutions.. :( – cwap Mar 01 '11 at 20:46
  • I hear you, there is no limit to how close to the "right" solution one can get. I guess we have to make due with our best until we learn a better way :) – e36M3 Mar 02 '11 at 15:39
  • +1 - I am struggling with this as well. Before I had separate repo and service layer for every table. I started to combine these where it made sense, but then I ended up with a repo and service layer with over 1k lines of code. In my latest application slice, I have backed up a bit to only put closely related concepts in the same repo/service layer even if that item is dependent. eg - for a blog, I was adding comments to the post repo aggregate, but now I have separated them out to separate comment repo/service. – jpshook Jun 30 '11 at 18:50

5 Answers5

12

You are allowed to have any method you want in your repository :) In both of the cases you mention, it makes sense to return the user with phone list populated. Normally user object would not be fully populated with all the sub information (say all addresses, phone numbers) and we may have different methods for getting the user object populated with different kind of information. This is referred to as lazy loading.

User GetUserDetailsWithPhones()
{
    // Populate User along with Phones
}

For updating, in this case, the user is being updated, not the phone number itself. Storage model may store the phones in different table and that way you may think that just the phones are being updated but that is not the case if you think from DDD perspective. As far as readability is concerned, while the line

UserRepository.Update(user)

alone doesn't convey what is being updated, the code above it would make it clear what is being updated. Also it would most likely be part of a front end method call that may signifiy what is being updated.

For the lookup tables, and actually even otherwise, it is useful to have GenericRepository and use that. The custom repository can inherit from the GenericRepository.

public class UserRepository : GenericRepository<User>
{
    IEnumerable<User> GetUserByCustomCriteria()
    {
    }

    User GetUserDetailsWithPhones()
    {
        // Populate User along with Phones
    }

    User GetUserDetailsWithAllSubInfo()
    {
        // Populate User along with all sub information e.g. phones, addresses etc.
    }
}

Search for Generic Repository Entity Framework and you would fine many nice implementation. Use one of those or write your own.

amit_g
  • 28,825
  • 7
  • 54
  • 111
  • @amit_g, thanks for the info. I already utilize a generic/base repository from which all others inherit. My idea for a logical grouping of "lookup" tables into one repository was simply to save time and cut down on number of repositories. So instead of creating ColorCodeRepository and AnotherCodeRepository, I would simply create CodesRepository.GetColorCodes() and CodesRepository.GetAnotherCodes(). But I'm not sure if a logical grouping of unrelated entities into one repository is bad practice. – e36M3 Mar 01 '11 at 19:24
  • Also, you are then confirming that by DDD rules, the methods in a repository corresponding to the root should be returning the root and not underlying entities within the graph. So in my example any method on the UserRepository could only return User type, regardless of what the rest of the graph looks like (or the part of the graph I'm really interested in such as Addresses or Phones)? – e36M3 Mar 01 '11 at 19:28
  • CodesRepository is fine but it would be difficult to maintain consistently what belongs in that. The same can be fulfilled simply by GenericRepositoryGetAll(). Since GenericRepository would have only very generic methods (GetAll, GetByID etc), it would work just fine for the Lookup tables. – amit_g Mar 01 '11 at 19:47
  • As far as possible the methods in repos should return root objects but if needed can return non-root objects as well say GetAllUserPhones(userID). The most important point of aggregate root repository is to not have repository for each entiry. – amit_g Mar 01 '11 at 19:49
  • @amit_g, I think I see what you mean. You're suggesting that the GenericRepistory would not simple act as something that other repositories can inherit from (abstract), but also I should be able to instantiate it and simply use its generic methods for basic consumption of unrelated tables such as lookup of codes. Is that correct? – e36M3 Mar 01 '11 at 19:50
  • 1
    @e36M3, Yes. For example http://geekswithblogs.net/seanfao/archive/2009/12/03/136680.aspx – amit_g Mar 01 '11 at 20:06
  • Isn't the point of lazy loading that you don't have to call methods like GetUserDetailsWithPhones - your ORM would populate that on first usage (e.g. when you update it with the new number)? – A. Murray Sep 17 '13 at 15:20
  • 2
    Unfortunately this answer is wrong. Repository should be treated as a collection of in-memory objects and you should avoid lazy loading. Here is nice article about that http://besnikgeek.blogspot.com/2010/07/specification-pattern-and-complex.html – Rafał Łużyński Oct 10 '14 at 13:36
10

Your example on the Aggregate Root repository is perfectly fine i.e any entity that cannot reasonably exist without dependency on another shouldn't have its own repository (in your case Phone). Without this consideration you can quickly find yourself with an explosion of Repositories in a 1-1 mapping to db tables.

You should look at using the Unit of Work pattern for data changes rather than the repositories themselves as I think they're causing you some confusion around intent when it comes to persisting changes back to the db. In an EF solution the Unit of Work is essentially an interface wrapper around your EF Context.

With regards to your repository for lookup data we simply create a ReferenceDataRepository that becomes responsible for data that doesn't specifically belong to a domain entity (Countries, Colours etc).

Darren Lewis
  • 7,998
  • 1
  • 31
  • 55
  • 1
    thank you. I am not sure how Unit of Work replaces the repository? I already employ UOW in a sense that there will be a single SaveChanges() call to the Entity Framework context at the end of every business transaction (end of HTTP request). However I still go through Repositories (that house the EF context) for data access. Such as UserRepository.Delete(user) and UserRepository.Add(user). – e36M3 Mar 01 '11 at 19:34
5

If phone makes no sense w/o user, it's an entity (if You care about it's identity) or value object and should always be modified through user and retrieved/updated together.

Think about aggregate roots as context definers - they draw local contexts but are in global context (Your application) themselves.

If You follow domain driven design, repositories are supposed to be 1:1 per aggregate roots.
No excuses.

I bet these are problems You are facing:

  • technical difficulties - object relation impedance mismatch. You are struggling with persisting whole object graphs with ease and entity framework kind a fails to help.
  • domain model is data centric (as opposed to behavior centric). because of that - You lose knowledge about object hierarchy (previously mentioned contexts) and magically everything becomes an aggregate root.

I'm not sure how to fix first problem, but I've noticed that fixing second one fixes first good enough. To understand what I mean with behavior centric, give this paper a try.

P.s. Reducing repository to aggregate root makes no sense.
P.p.s. Avoid "CodeRepositories". That leads to data centric -> procedural code.
P.p.p.s Avoid unit of work pattern. Aggregate roots should define transaction boundaries.

Arnis Lapsa
  • 39,296
  • 27
  • 111
  • 190
  • 1
    As the link to the paper is no longer active, use this one instead: http://web.archive.org/web/20141021055503/http://www.objectmentor.com/resources/articles/CoffeeMaker.pdf – JwJosefy Jul 04 '16 at 17:21
3

This is an old question, but thought worth posting a simple solution.

  1. EF Context is already giving you both Unit of Work (tracks changes) and Repositories (in-memory reference to stuff from DB). Further abstraction is not mandatory.
  2. Remove the DBSet from your context class, as Phone is not an aggregate root.
  3. Use the 'Phones' navigation property on User instead.

static void updateNumber(int userId, string oldNumber, string newNumber)

static void updateNumber(int userId, string oldNumber, string newNumber)
    {
        using (MyContext uow = new MyContext()) // Unit of Work
        {
            DbSet<User> repo = uow.Users; // Repository
            User user = repo.Find(userId); 
            Phone oldPhone = user.Phones.Where(x => x.Number.Trim() == oldNumber).SingleOrDefault();
            oldPhone.Number = newNumber;
            uow.SaveChanges();
        }

    }
Chalky
  • 1,435
  • 14
  • 20
  • Abstraction is not mandatory, but is recommended. Entity Framework is still just a provider and part of the infrastructure. It is not even a matter of only what were to happen if the provider changed, but in larger systems that you might have multiple types of providers that persist different domain concepts to different persistence mediums. This is the sort of abstraction that is extremely easy to make early on, but painful to refactor towards over enough time and complexity. – Joseph Ferris Aug 05 '16 at 20:24
  • 2
    I have found it very difficult to retain the benefits of EF'S ORM (e.g. lazy loading, queryables) when I try and abstract to a repository interface. – Chalky Aug 06 '16 at 06:58
  • It is an interesting discussion, to be sure. Since lazy loading is very specific to the implementation, I find its value is limited to the infrastructure (domain object in and out with layer bounded translation). A lot of the implementations that I have seen run into issues when a generic abstraction is attempted. I tend to go with an explicit implementation, because the generic methods have very little domain value. EF does make queryables highly usable, but the problem becomes the role of the repository - i.e. repositories used by controllers miss the benefits of abstraction. – Joseph Ferris Aug 06 '16 at 13:42
0

If a Phone entity only makes sense together with an aggregate root User, then I would also think it makes sense that the operation for adding a new Phone record is the responsibility of the User domain object throught a specific method (DDD behavior) and that could make perfectly sense for several reasons, the immidiate reason is we should check the User object exists since the Phone entity depends on it existence and perhaps keep a transaction lock on it while doing more validation checks to ensure no other process have deleted the root aggregate before we are done validating the operation. In other cases with other kinds of root aggregates you might want to aggregate or calculate some value and persist it on column properties of the root aggregate for more efficient processing by other operations later on. Note though I suggest the User domain object have a method that adds the Phone it doesn't mean it should know about the existence of the database or EF, one of the great feature of EM and Hibernate is that they can track changes made to entity classes transparently and that also means adding of new related entities by their navigation collection properties.

Also if you want to use methods that retrieve all phones regardless of the users owning them you could still though it through the User repository you only need one method returns all users as IQueryable then you can map them to get all user phones and do a refined query with that. So you don't even need a PhoneRepository in this case. Beside I would rather use a class with extensions method for IQueryable that I can use anywhere not just from a Repository class if I wanted to abstract queries behind methods.

Just one caveat for being able to delete Phone entities by only using the domain object and not a Phone repository you need to make sure the UserId is part of the Phone primary key or in other words the primary key of a Phone record is a composite key made up of UserId and some other property (I suggest an auto generated identity) in the Phone entity. This makes sense intuively as the Phone record is "owned" by the User record and it's removal from the User navigation collection would equal its complete removal from the database.

Kaveh Hadjari
  • 217
  • 1
  • 10