Following this description of the Repository Pattern, we have three main concerns that need their own classes:
- The "Repository," which takes and returns Domain Models.
- The "Gateway," which takes data from the Domain Model and returns some sort of generic data
- 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:
- A "Gateway" for interacting with persistent storage
- 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?