7

Following this description of the Repository Pattern, we have three main concerns that need their own classes:

  1. The "Repository," which takes and returns Domain Models.
  2. The "Gateway," which takes data from the Domain Model and returns some sort of generic data
  3. The "Factory," which takes the "generic" data from the Gateway and maps it to the Domain Model

A Repository needs two objects in order to do its job:

  1. A "Gateway" for interacting with persistent storage
  2. A "Factory" to translate data from the Gateway into the Domain Model

When dealing with strongly typed languages, every variable needs a "Type." If you specify that Type and use it in your Repository, you are coupling it to the underlying data model of the persistence layer, and then passing it on to the Factory, but the examples I find show the Repository dealing directly with both the Gateway and Factory.

An example of the interfaces for a "Blog"

public interface IBlogGateway
{
    // Returns generic data type so switching persistence
    // does not require refactoring in the Repository
    object Find(long id);
}

public interface IBlogFactory
{
    // Accepts generic data type so switching persistence
    // does not require refactoring in the Repository
    Blog Make(object data);
}

public interface IBlogRepository
{
    Blog Find(long id);
}

And the implementation of the interfaces:

public class SqlBlogGateway : IBlogGateway
{
    public object Find(long id)
    {
        DataTable table = // Find from database
        DataRow row = table.Rows.Count == 1
                    ? table.Rows[0]
                    : null;

        // Implicit cast from DataRow to object (this code smells)
        return row;
    }
}

public class SqlBlogFactory : IBlogFactory
{
    public Blog Make(object data)
    {
        // Explicit cast from object to DataRow (this code smells)
        DataRow row = (DataRow)data;
        Blog blog = new Blog(long.Parse(row["ID"].ToString()))
        {
            Title = row.Field<string>("TITLE")
        };

        return blog;
    }
}

public class BlogRepository : IBlogRepository
{
    private IBlogFactory factory;
    private IBlogGateway gateway;

    public BlogRepository(IBlogFactory factory, IBlogGateway gateway)
    {
        this.factory = factory;
        this.gateway = gateway;
    }

    public Blog Find(long id)
    {
        object data = gateway.Find(id);

        if (data == null)
            return null;

        return factory.Make(data);
    }
}

Specifically I'm working with C# in .NET, but this is applicable to any strongly typed object oriented language. When persisting to a database, we could deal with DataSet, DataTable and DataRow objects (or array for PHP or HashTable for Java). When it comes time to refactor the "Gateway" to persist to a web service those Types will not suffice, because a web service is not likely to use a DataTable. Instead it will specify it's own Data Transfer Object.

You could have the Gateway return an object type, and the "Factory" take an object type and cast it to the proper type, but then you lose all the benefits of a strongly typed language, such as compile time checking and Intellisense/Auto-Complete in an IDE. Further more if you cast up to object then back down to a type specific to the Gateway, you really can introduce some funky, hard to debug runtime errors. It feels like the Repository needs a Gateway, and a Gateway needs a Factory, but the Repository shouldn't know about the Factory.

To eliminate the need for refactoring the Repository when changing persistence mechanisms, what data type should the Gateway return?

3
  • 1
    although you can logically break down a repository into these parts. I'm not sure it makes sense to do so. The repo provides abstraction from the persistence implementation, what does further internal abstraction add?
    – Ewan
    Oct 26, 2015 at 16:44
  • @Ewan: To be honest, I have the same question. The reason for decoupling further could be to implement a cascading delete, for instance. Before deleting the parent object, you make calls to additional repositories to delete the child objects. Although you could say that's a concern for the Gateway. I was just modeling my implementation of the repository pattern after the one I linked to in my question. Oct 26, 2015 at 16:50
  • the article seems to forget about deleting
    – Ewan
    Oct 26, 2015 at 16:54

3 Answers 3

4
+100

Ideally, you only want your Gateway class to know what persistence back-end it is talking to, and all the other parts should be agnostic to the back-end. Unfortunately, you are finding that, given this particular implementation, the Factory class cannot be agnostic because it needs to know what type of object it is getting.

You mentioned that you can't just return a DataTable, etc. because a non-database persistence back-end wouldn't use these structures. Can we instead pass an agnostic representation out of the Gateway? Let's look at some options:

  1. Return a DataTable. If you are only going to use database-backed persistence, just do this. It handles a lot of the details for you while being generic across database engines. You don't want to do this because you are worried that a non-database back-end doesn't use this.

  2. Return a simple, custom DTO object from the Gateway. For the IBlogGateway, it might return a BlogDTO object with just data members. This means more classes, but you can return an object with all the correct types already in place. This also creates a dependency on the DTO class in all of the other classes, but this dependency is reasonable since they all already deal with Blog and BlogDTO will mirror that dependency.

  3. Return a generic container that can be created from any type of persistence. For example, we can convert the DataTable to an IEnumberable<Dictionary<string, object>>, where the enumerable holds the database rows (or web service results, etc.) represented as Dictionarys mapping keys to values. We lose some type information since all the values are objects, but DataRow has a similar limitation that seems otherwise acceptable. This is comparable with the way dynamic languages return results (such as PHP returning an array of associative arrays).


I want to also point out (as @Ewan noted in a comment to the question) that the Gateway, Factory, and Repository don't strictly need to be distinct classes/interfaces. A repository can fill the role of all three, especially when starting out. Later, we can refactor out parts as needed. Even if the gateway is factored out, often the factory and repository can be the same class since they have closely related responsibilities (creating objects of a type).

Contrarily, separating the classes out provides the opportunity for composition, allowing (for example) the gateway to vary in isolation from the rest of the code. Different gateway implementations, corresponding to different back-ends, can be composed in at runtime (and test time) for greater flexibility.

2
  • See, this answer is kind of reinforcing my gut feeling that I'm not quite implementing this properly. The one thing I'm questioning is whether or not the Factory should be a part of the Repository. Since the Factory and Gateway are so closely tied together, the Factory seems like a dependency of the Gateway, not the Repository. Oct 26, 2015 at 17:19
  • StackOverflow question related to converting DataTable objects to Dictionaries: Transform a DataTable into Dictionary C# Oct 26, 2015 at 17:24
2

Examples written in Java. I think something similar can be done in C#.

public interface GenericRow {
  String getString(String fieldName);
  Integer getInteger(String fieldName);
  Long getLong(String fieldName);
  Timestamp getDate(String fieldName);
  // getters for other types
}

public interface BlogGateway {
  GenericRow find(long id);
}

The same, but with some degree of compile-time type-safety (more elegant IMHO).

public interface Row<T extends Enum<T>> {
  String getString(T field);
  Integer getInteger(T field);
  Long getLong(T field);
  Timestamp getDate(T field);
}

public enum BlogField {
  TITLE,
}

public interface BlogGateway {
  Row<BlogField> find(long id);
}
1
  • 1
    Interesting. This seems to answer a couple questions, actually: 1) Where do I put the canonical source of field names; 2) How do I make the data "generic"? Oct 26, 2015 at 17:20
0

There is one instance where I can see that adding theses extra Gateway and Factory layers could help. That is when you are creating a CRUD style repo per Model

This is often seen with the use of generics. ie

Repo<Customer> repo = new Repo<Customer>();
Customer c = repo.GetById("1");

With this pattern you can see that you can create a generic Repo class if you also have generic Factories/Gateways for the various objects.

In this case they could use/return either the non sql specific DataRow a dictionary of Key Value pairs or similar.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.