Sunday, August 2, 2009

One Small Problem With Repositories

It used to be that I wrote applications with a hard separation between layers; presentation calls business, business calls data, and other systems were wrapped in abstraction as needed. This worked well enough and many large enterprise applications have been build this way.

One technique frequently implemented in this paradigm is that the entry points for the persistence routines of domain objects are encoded in the objects themselves. While not perfect, one great upshot of this approach was that there was only one place where the relationships between business objects was defined. E.g., if an Order is saved, the Order enforces that all of itsLineItems are also saved. The code is written once and tested once. If new repositories are needed for a new database, write the code, swap them out and as long as your business layer tests still pass, you know your relationship persistence is in tact. The biggest problem with this paradigm though is that it leads to difficult to write tests for oh so many different reasons.

As test driven development is the way to go, I haven't written applications like that for a couple of years. I have been sharpening my saw and forcing myself to build more testable code. In ASP.NET MVC, the prevailing winds seem to push in the direction that your controllers should be injected with repository instances and those instances should be used for data management. This works exceptionally well from the stand point that I can pass repository mocks and stubs to my controller when testing actions. This is just a better way to write code in general.

But one part of all this newer-fangeld code crafting still nags at me. If the repository contains the relationship logic, that logic and all of the tests that enforce it travel with that implementation. If I need to write a new data layer for a new database, I need to rebuild that logic and the tests for it and maintain multiple pieces of code that ultimately enforce the same rules. In almost all cases, this is not a real world problem. Most people never port their applications to multiple databases and so the interfaces are relegated to testing purposes only. Most of us only ever have one set of repository code to test. I'll live with it by ignoring it.

The perfectionist in me still wants to do something about it. I've thought about crazy schemes like dividing the repository functionality into multiple components, CUD/R if you will, with methods targeted to the domain model and UI respectively. I've thought about abstractions and composites and delegates, oh my! But in the end, every solution solves some problems and creates others. In the end, I suppose that knowing when good enough is good enough and being able to identify when it isn't is the way to handle this situation.

No comments:

Post a Comment