Question

The repository pattern suggest that you can only pull aggregate roots. But how would you retrieve a single child using only it's uniqiue identity(Child.ID) if you do not know it's parent(root)?

class Parent
{
    public int ID { get; set; }
    IEnumerable<Child> Children { get; private set; }
}

class Child
{
    public int ID { get; private set; }
    public virtual Parent Parent { get; private set; } // Navigational model
}

My application is stateless (web), for simplicity, the request only contains the ID of the child.

I am thinking three approaches:

  1. Call all the parents then ask them politely who owns this child.
  2. Have a special routine in the ParentRepository called get GetChildByID, which kinda fails the repository's abstraction.
  3. Modify the request to include the parent, but seems unnecessary since you already have a unique identity.
Was it helpful?

Solution

It seems likely that you're actually looking at a different bounded context here. You mentioned in your question that "repository ... can only pull aggregate roots."; this is correct. Another answer also mentions that if you need to query a child object, the child object may also be an aggregate root. This may also be correct within a different bounded context. It's quite possible for an entity to be an aggregate root in one context, and a value entity in another.

Take for example the domain of Users and the mobile/tablet Apps they have installed on their devices. In the context of the user, we might want the users basic properties such as name, age etc, and we might also want a list of apps the user has installed on their device. In this context User is the aggregate root and App is a value object.

bounded context UserApps
{
    aggregate root User
    {
        Id : Guid
        Name : string
        Age : int
        InstalledApps : App list
    }

    value object App
    {
        Id : Guid
        Name : string
        Publisher : string
        Category : enum
    }
}

In another context we may take an App centric view of the world and decide that App is the aggregate root. Say for example we wanted to report which users have installed a given app.

bounded context AppUsers
{
    aggregate root App
    {
        Id : Guid
        Name : string
        InstalledBy : User list
    }

    value object User
    {
        Id : Guid
        Name : string
        InstalledOn : Date
    }
}

Both of these bounded contexts would have their own repository which returns the respective aggregate root. There's a subtle but crucial difference in your perspective of the data.

I think if you take a step back and think about why you want to query for a child object, you might find that you're actually in an entirely separate bounded context.

OTHER TIPS

If you require the child for display/reporting/viewing/reporting then a simple query layer will do.

If you are manipulating the child in any way then it has a consistency boundary and it sounds an awful lot like an aggregate.

Try not to query your domain objects. Another simple rule of thumb is not not include an aggregate reference in another aggregate but to rather use just the referenced aggregate's Id or even a value object representing the relationship.

Entity navigation is not a purpose of a domain model.
An Aggregate Root is a composition of Entities and Values that expose business operations.
As a side effect, you can still perform some simple query or navigation through your AR, but, for complex querying, creating and using a Query Model is more efficient.
I'm talking about CQRS.

Hope this can help you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top