Keeping the Domain Model free from Persistence Logic

A system with a complex domain model often benefits from isolating the domain objects from persistence logic. Your domain objects are then Plain Old CLR Object (POJO), or PI-O (Persistence Ignorance-Objects). In my case I want the decision about which infrastructure to choose for persistence support to be made as late as possible, and to be dealt with iteratively. One possible solution is the Repository Pattern [Fowler, PoEAA] which mediates between the domain and data mapping layers using a collection-like interface for assessing domain objects. Also, repositories help you reduce object access by creation and traversal to keep your domain model clean from muddling associations and endless tangles. Another advantage is the decoupling from the client which leaves you with more freedom to change the implementation of the repository. You can take advantage of this to optimize for performance, by varying the query technique or by caching objects in memory. Performance is one of the reasons to rewrite large portions of our DNA n-tier focused web application. From a Domain Driven Design [Evans, DDD] point of view repositories belong to the domain model. Then how do you, since repositories use the persistence infrastructure, maintain the low coupling between the domain model package and persistence package? Lets picture a solution based on the Repository Pattern.

The domain model contains a Customer domain object and a CustomerRepository which defines one operation ForCustomerId and returns the customer with the specified id. The CustomerRepository strategy is to delegate to the persistence infrastructure CustomerMapper to get the job done. As you can see in this very simple example is that there is a bidirectional relationship between the domain and persistence packages. Do I hate cyclic references!


A lot of ideas presented in the next picture can be found in Steve Maine’s post. While I discussed this with Jimmy Nilsson he came up with two (preferred) solutions.
a.) Put the repositories in another assembly, but think of them as conceptually belonging to the Domain Model.
b.) Put the repositories in the Domain Model assembly and use an abstraction layer which the repositories are programmed against.

I don’t like the idea of another abstraction layer since it tends to make the persistence infrastructure even harder to use (efficiently with level of indirection). Putting the repositories in a separate assembly is the Separated Interface Pattern [Fowler, PoEAA]. Separated Interface defines an interface in a separate package from its implementation.

Sidenote: Experienced developers (from the COM/DCOM era) would possibly extract an interface for each domain object and put those in a separate package. The persistence package would only have to know about the interfaces package and instantiate implementations using reflection. And thus skip the whole Repository & Factory solution. I think this is excessive, not only because you (normally) have far more domain objects then repositories, but also because implementation changes in domain objects often require the interface to change as well. The dollar cost of continuous learning/modifying and refactoring rises and shouldn’t discourage developers from executing these best practices.

One awkward thing about separated interfaces is how to instantiate the implementation. I use a separate factory object, RepositoryFactory which returns Repository instances to the client. To bind an implementation to the RepositoryFactory I use the Plugin Pattern. Since the .NET Framework has great support for reflection I dynamically load the assembly containing the repository implementation and use the Activator class to create an instance.


Rock solid, comments?

9 Comments

  • and what does this really gain you? A lot of extra work. :)



    Be aware that the .NET world isn't comparable with the java world. In the java world, there are standards like EJB-CMP, so having POJO objects is key. In .NET it's not, you need non-POCO objects to get decent databinding, remoting and xml serialization (cyclic references, interface types etc.)



    Furthermore, it looks nice in theory, but in practise, your application is tied to the O/R mapper chosen, through the query system and mechanism your O/R mapper uses to presents itself to you, like through a session, a context, an adapter, a combination? ..



  • Thanks for the comments!



    > and what does this really gain you? A lot of extra work. :)



    The repository pattern encapsulates the data retrieval technology to bring back our model focus. Don’t get me wrong, I have nothing against working with an OR Mapper based on inheritance or by implementing an interface. My starting point is the domain model and I force myself to keep the domain model free from any persistence awareness.



    > you need non-POCO objects to get decent databinding, remoting and xml serialization (cyclic references, interface types etc.



    Domain objects don’t implement data binding. Mats Helander wrote a couple of Mapping Mania articles.



    > Furthermore, it looks nice in theory, but in practise, your application is tied to the O/R mapper chosen, through the query system and mechanism your O/R mapper uses to presents itself to you, like through a session, a context, an adapter, a combination? ..



    Totally agree! Does this mean you should stop caring about loose coupling or vendor lockin?



    > what about having a query subsystem that you can use to query for a customer by an arbitrary condition



    The preferred solution Query Object, OPATH, OQL, OCL etc. I really don’t want to care about querying in the domain model. If the going get tough the repository implementation may lie as close as possible to the database or XML (whatever storage mechanism you use) to stay performant. Under the covers, repository combines metadata mapping with a query object or in my case datamapper (since I’m using IBatis for the time being).



    > In general, if you have, let's sy, 200 or so business objects, you end up with 600 or so useless classes.



    Only aggregates have a repository. The CustomerRepository could have a for



    > Bad news: this is a bad designed persistence layer.



    This post isn’t about a persistence layer. Perhaps I should have stayed away from drawing that persistence package and mapper class.

  • Well, there is no single answer of course. It's a matter of pros and cons.



    I know, I'm kicking in open doors, but I just had to say that.

    :-)



    Best Regards,

    Jimmy

    www.jnsk.se/weblog/

    ###

  • I don't see benefits in Repository level as well. it is better to use O/R mapper and IoC framework to provide correct implementations of DAO classes. Thus you may have separate DAO layer for different O/R mappers (or even pure Dynamic SQL). While this is not an easy thing, it is doable. And session and context may be a problem, but it depends on implementation.

  • In C#, I think you can use reflection to implement the mapping between tables and objects with Meta data. Then you needn't write Mapper class for every domain object.

  • Michael, could you elaborate more on separate DAO layers for different OR Mappers?



    Taken from the patternshare.com site:



    A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers.

    From the domain perspective, as I intended this post, Repositories definitely add value.



    Chen Li, IBatis uses reflection. I agree that having a mapper for each domain object involves a lot of work, but this is the price to pay for flexibility. Since this project has explicit performance requirements we need this level of flexibility/tweak ability.

  • I agree with Frans. It is good in theory. It might work for simple queries, unless you want to write your own predicate expression logic (which is a pain to reinvent the wheel) for complex queries, the Domain layer will need to send the predicate expression of the ORMapper which makes the Repository obsolete.

  • You could make the data mapper just return something like a DataReader or a DataTable/DataSet, and then pass that data to a factory/builder to build your domain objects.

  • I like the concept, but I don't see this working the way you've implimented it. I noticed that the factory returns type Repository which means that any class that impliments the Repository interface will be returned. However, the only methods that will have visibility at that point will be methods defined in the Repository interface (in your diagram there are none). Basically what i'm saying is that the following wouldn't compile:

    Repository repository = RepositoryFactory.CreateRepository(CustomerRepository);

    Customer customer = repository.ForCustomerId(id); //Error on this line

    In order for this to work the CustomerRepository interface would have to inherit from the Repository interface. I'm assuming you just left this out. Could you clarify?

Comments have been disabled for this content.