Tuesday, July 12, 2011

How Do You Handle Aggregates At Your Shop?

I've witnessed this one too many times.

So here's a problem that I've commonly experienced during development. You get requirements, analyze them, and start to implement your domain model based on those requirements. If you're an agile shop, those requirements will come in chunks and be implemented in sprints or iterations (whatever you wanna call it). So your initial set of requirements will only have a part of the big picture. Let's say you're going to make logic for users and profiles. To start out with, a user simply wants to view their profile. So what might some of you proceed to do? You'll only make the ProfileEntity and leave the user out if you can get away with it. Why? Well let's say when you go to the profile page, you don't need to render anything about the user; stricly profile info (let's assume for the sake of brevity that you haven't implemented UserRepository and if you have, you're still not going to use it). So you do something like....

public class ProfileRepository {
    public Profile Get(Guid userId) {
        // call NHibernate or RavenDb, YEAAAA!!
        return session
                .Query<Profile>(p => p.UserId == userId)
                .SingleOrDefault();
    }
}

Now someone like myself would say hey, aren't we in the context of the user? Isn't the user the aggregate root? Shouldn't we be doing...

public class UserRepository {
    public User Get(Guid userId) {
        // call NHibernate or RavenDb. yea!!
    }
}

// dependency inject this of course
var userRepo = new UserRepository();
var user = userRepo.Get(Guid.NewGuid());
var profile = user.Profile;

// map profile to view model if you want
return View(profile);

I'd rather be proactive about making my aggregates if I can help it. Even if they aren't immediately needed. Heck, if you want to make a bare UserEntity with nothing but it's associations until you later find out its specific properties then that doesn't hurt either. At least you'll be prepared once you have to account for the user's information.

What ends up happening is that you have a proliferation of mini repositories that should never be there in the first place. Essentially one per association. When in reality you should be fetching the aggregate and drilling down into it to pick off associations. Since you should never access the associations without first going through the aggregate. That's if you're adhering to DDD.

So instead of implementing ProfileRepository and AddressRepository that both know how to retrieve profiles and addresses by userId, you end up with one wholesome UserRepository that will serve users and their respective associations. Like orders, address, profile, etc. as I've outlined below.

But some will do what is only necessary and refactor later on. The one inherent problem is that in almost every situation, we're always in the context of a user. Which kind of leads to a question within this one. Should everything hang off of the core user object? Think of how many associations that would be.

public class User {
    public Profile Profile { get; set; }
    public Address Address { get; set; }
    public IList<Orders> Orders { get; set; }
    public IList<Comment> Comments { get; set; }

    // and so on and so forth
}

For really big applications, upon hitting Intellisense, you'd be bombarded by about 30+ properties. I guess that's not so bad. You could always house them within some other contextual object. Something like...

User.UserInfo.Profile and User.UserInfo.Address

I dunno.