Repositories, factories and hierarchically structured data
Asked Answered
S

3

10

I am going through Domain Driven Design by Eric Evans where he outlines the interplay between Repositories and Factories. The repository itself will call an DB interface to get a result set. This result set would then be passed to a factory that would understand that result set to reconstitute the object.

What if the data was hierarchical in nature, like some sort of tree structure. For example:

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Foo Parent { get; set; }
    public ICollection<Foo> { get; set; }

    // Other business like methods here

}

Using DDD I would have my interfaces and implementations:

public interface IFooRepository
{
    Foo Get(int id);
}

public interface IFooFactory<TSource>
{
    Foo Create(TSource source);
}

public class SqlFooRepository: IFooRepository
{
    private readonly IFooDao dao;
    private readonly IFooFactory<SqlDataReader> factory;

    public SqlFooRepository(IFooDao dao, IFooFactory factory)
    {
        this.dao = dao;
        this.factory = factory;
    }

    public Foo Get(int id)
    {
        var resultSet = dao.Find(id);
        return factory.Create(resultSet);
    }
}

public class SqlFooFactory: IFooFactory<SqlDataReader>
{
    public Foo Get(SqlDataReader reader)
    {
        var foo = new Foo();
        foo.Id = (int) reader["id];
        foo.Name = (string) reader["name"];
            // Do I build the children accounts here
        return foo;
    }
}

If I try to build the children in the factory then I need access to the repo again. If I do it in the Repo I feel like I am doing work that should be for the factory. Not sure how to tackle this one.

One thought I had is that the Foo is not the aggregate root but rather the FooTree is the aggregate root. So trying to get any Foo I would need to create the entire tree, which means I could pass a collection of Foo objects to a FooTreeFactory.

Any help would be very appreciated.

Schwinn answered 6/12, 2013 at 12:33 Comment(2)
Get method you can build Foo and the all related children;You shall load all requried records from DB in a SQL query and in a generic way build your tree(You have all required data to do that e.g. ParentId, ChildId and to build your tree); You do not have to talk here to your repositories or any another layer except the backend!Malayoindonesian
Can you perhaps elaborate and do it in the form of an answer. I can then mark you as the accepted answer if seems to be the best fit.Schwinn
N
3

Assuming you need all children when working wit a Foo, you should fetch them in the repository and reconstruct the hierarchy in your factory. If you don't necessarily need them; or you might need them in some corner case scenarios, you could consider fetching them lazily, but I assume that this is not what you're after here.

When constructing a hierarchy like this, you want to make sure to only hit the database once. For instance, if you'd fetch Foo foo1 in one call to the db and then fetch the children of foo1 using the same repository method repo.Get(foo1.Id), then you'd have an additional db roundtrip for each child ... and then some more if you this recursively for each child too. You don't want this, because it would result in an unknown number of additional database round trips (a variant of the select N+1 problem).

What you want, is a repository that fetches your complete hierarchy in one database roundtrip. If you are using an ORM, then often the ORM has something build in to handle this for you; for instance NHibernate has a DistinctRootEntityResultTransformer to do exactly this.

If you want to this with a plain-sql repository, then I'd create a stored procedure (assuming you are using Sql Server) that fetches all Foo rows in the hierarchy recursively from the database and returns them in one result set to the repository. The repository then passes this result set to your factory to create the object tree.

So the key is not to pass a single Foo to your factory, but instead pass a reader to the factory that reads a result set of Foo rows, instead of a single row.

Update

After re-reading your question, I think you and @enrico are spot on:

[...] the FooTree is the aggregate root. So trying to get any Foo I would need to create the entire tree, which means I could pass a collection of Foo objects to a FooTreeFactory

Nicolettenicoli answered 15/12, 2013 at 11:27 Comment(0)
F
4

Repositories handles Aggregate Roots, not Entities. So, I'd suggest you to go with the FooTree AR solution and retrieve it from the db. The Factory should not depends on the Repository, so you have to grab all tree data and pass them to the factory. Alternatively, you can implement something like ORM's lazy loading and "lazy load" children when the AR client (or the AR itself) requests them.

Fulbright answered 9/12, 2013 at 11:41 Comment(0)
N
3

Assuming you need all children when working wit a Foo, you should fetch them in the repository and reconstruct the hierarchy in your factory. If you don't necessarily need them; or you might need them in some corner case scenarios, you could consider fetching them lazily, but I assume that this is not what you're after here.

When constructing a hierarchy like this, you want to make sure to only hit the database once. For instance, if you'd fetch Foo foo1 in one call to the db and then fetch the children of foo1 using the same repository method repo.Get(foo1.Id), then you'd have an additional db roundtrip for each child ... and then some more if you this recursively for each child too. You don't want this, because it would result in an unknown number of additional database round trips (a variant of the select N+1 problem).

What you want, is a repository that fetches your complete hierarchy in one database roundtrip. If you are using an ORM, then often the ORM has something build in to handle this for you; for instance NHibernate has a DistinctRootEntityResultTransformer to do exactly this.

If you want to this with a plain-sql repository, then I'd create a stored procedure (assuming you are using Sql Server) that fetches all Foo rows in the hierarchy recursively from the database and returns them in one result set to the repository. The repository then passes this result set to your factory to create the object tree.

So the key is not to pass a single Foo to your factory, but instead pass a reader to the factory that reads a result set of Foo rows, instead of a single row.

Update

After re-reading your question, I think you and @enrico are spot on:

[...] the FooTree is the aggregate root. So trying to get any Foo I would need to create the entire tree, which means I could pass a collection of Foo objects to a FooTreeFactory

Nicolettenicoli answered 15/12, 2013 at 11:27 Comment(0)
I
2

Do not reference other AggregateRoot when designing Aggregate.

So in common case, A Foo will not reference other Foos as tree or parent. This may be a query requirement if you do need this. For example, displaying data. DDD is not good at query, so it's easier to implement this with anemic models without so many DDD limits and rules.

UPDATE
You may consider Specification pattern if this hierachy is used for domain derivations.

public class FooBarSpecification
{
    public Foo Parent //injected by constructor
    public ICollection<Foo> //injected by constructor

    public boolean isSatisfiedBy(Foo foo) {
        //use FooTree here to
    }

}

The Client may use FooRepository to get the Foos to initiate the Specification.

Another solution is using DomainService.

Infra answered 6/12, 2013 at 13:7 Comment(2)
But I have some methods on the Foo objects that will call other methods on the Foo parent to do its work. This is why the tree was necessary. UPDATE: In the model the fact that this relationship exists is very important. How do I model this relationship then?Schwinn
@Schwinn Sorry, forget the domain derivation case. Answer updated.Infra

© 2022 - 2024 — McMap. All rights reserved.