4

Is client code allowed to reference entities within an aggregate that is not the root? I have a Story (Root), Team (Entity) and TeamMember (Entity). I am trying to decided if the AddTeamMember method belongs on the Team or Story.

I guess my example was a little mis-leading. My real question is can client code reference non-root entities within an aggregate?

ckittel
  • 6,478
  • 3
  • 41
  • 71
Marco
  • 2,453
  • 3
  • 25
  • 35

4 Answers4

3

My opinion - it should not. Having a reference to an entity that belongs to certain aggregate means that you are able to invoke a method on that entity without full aggregate context and if you allow that, you can never be sure if your entire aggregate is valid and consistent.

Simple example:

    public class MyAggregateRoot
    {
        protected MyEntity entity;

        public void BuildUpAggregate()
        {
            ValidateSomeRule();
            LoadEntityFromDatabase();
        }

        public MyEntity MyEntity
        {
            get 
            {
                VerifySomeOtherRule();
                return entity; 
            }
        }
    }

As you can see, while building and retrieving MyEntity via aggregate root we have two validation rules checked - if you would allow the client to reference MyEntity directly, the aggregate might change in time between client retrieved the entity and performed an operation on it, so the checks would no longer be valid, but you wouldn't event be aware of this fact. In other words, your aggregate would be inconsistent and potentially invalid.

In general, in DDD there's a strong rule that states that all access to entities in aggregate should be performed by traversing from the aggregate root - this rule is exactly for the sake of consistency of aggregates.

That said, what your client can reference is a projection of an entity - a sort of read-only copy that contains only data required for displaying and deciding whether a certain action is available in current context. You can go even further and aggregate the data from a set of entities to a single projection, tune it up a little, so it would be tailored to the requirements of UI. I found this technique quite useful for not allowing my UI to decide how the business domain should be modeled.

kstaruch
  • 1,289
  • 7
  • 7
  • +1 Do you think it makes sense to enforce this by hiding all non-aggregate-root entities with something like C# 'internal'? – Dmitry Aug 17 '11 at 22:56
  • @Dmitry: although I see benefits of such limitation, I'd rather enforce it on conceptual level - what I tend to do is to create Repositories for Aggregate Roots only and introduce a rule that you can only retrieve entity via Repository. The thing with hiding by 'internal' is that makes testing a little harder and it enforces your project's physical structure (I know, you can set visibility of internal classes in AssemblyInfo, but I consider it rather as a trick then solution). Plus it's harder to create proxies or mocks. Of course the approach depends on maturity of your team as well. – kstaruch Aug 18 '11 at 09:20
-1

Without knowing how your architecture is supposed to work, it sounds like it should go on Team instead of Story. That's assuming you have a 1 to many from Team to Team Member, and that Team Member is a child of the Team object, not Story.

Tejs
  • 40,736
  • 10
  • 68
  • 86
-1

I'm not sure about 'root' or 'aggregate' rules but I don't think a Story should know anything about how a Team's membership is managed.

n8wrl
  • 19,439
  • 4
  • 63
  • 103
-1

Team should have "has a" relationship with Team member. where as story and team member are not directly related to each other.

your class should look like:

class team
{
list<TeamMember> teamMembers;
void AddTeamMember(TeamMember member){}
}

another reason why Team must have add team member method, if the method is part of story then you have to explicitly mention in which team you want to add member.

addteamMember(Team T, TeamMember member);

Root should provide proper methods to access non root elements(first level of nesting). In your case Team should have addTeamMember method and story should have a method (AddMemberToTeam(team t, teammember m)) which calls addTeamMember method of specified team.

Tarang
  • 646
  • 4
  • 6
  • Got your point Marco. root should provide proper methods to access non root elements(first level of nesting). In your case Team should have addTeamMember method and story should have a method (AddMemberToTeam(team t, teammember m)) which calls addTeamMember method of specified team. – Tarang Aug 17 '11 at 18:28
  • 1
    got it. so if client code asks for say a specific team member, i should not return the actual entity, but possibly some other immutable object that is essentially a snapshot of the entity? – Marco Aug 17 '11 at 18:31
  • correct. only root should be allowed to manipulate its entities. – Tarang Aug 17 '11 at 18:37