Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repositories, factories and hierarchically structured data

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.

like image 298
uriDium Avatar asked Dec 06 '13 12:12

uriDium


2 Answers

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.

like image 58
Enrico Sanguin Avatar answered Oct 11 '22 04:10

Enrico Sanguin


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

like image 29
Marijn Avatar answered Oct 11 '22 04:10

Marijn