I've been working exclusively alone on a project for a while and have as much liberty as any developer could hope for. I'm a blessed man. Nonetheless, I have tried to kind of "pretend" like I am working in a team environment that has regimented deadlines. This is mostly due to integrity and partly due to prevention for laziness...how easy is it to constantly be researching solutions without implementing them?
There was a necessary period of growth in skill sets that had to occur and while I certainly haven't 'arrived' at some point the rubber has to hit the road and instead of striking out to google for an answer first, there must be some confidence on the toolkit that has been developed. A car has to at least be moving before turning the wheels even in the right direction will make any difference.
So design decisions whether regarding overall architectural structure or about simple object creation aren't strictly design decisions, but are really business decisions. Maintainability is almost at the top of my list for development ; partly because it's just fun to meet the challenge, but also because I work alone and I could be buried with feature requests if I am not careful to write robust, scalable code. Still, this goal has a way of stalling design decisions if you're not careful.
Realigning my thinking into Test Driven Development has largely stumped this phenomenon. Most importantly because TDD isn't about the tests, but meeting requirements in a iterative, thoughtful way.
An example of this came up for deciding how I am going to query my DB using NHibernate as my OR/M. Now, I am using an IRepository for controlling access to the NHibernate implementation of data access and this brings along a whole sweet suite (excuse me) of tools that are a snap to use. I need to create Query Objects to get the data I need in my domain, but do I really want to wrap so much that NHibernate already does for you? For example, do I need to wrap every ICriteria implementation with my own custom implementation to avoid the dependency on NHIbernate's API?How far does one take isolating a domain from specific implementation of these tools?
There seem to be at least three options:
- Have more specific IRepository<T> implementations that expose methods like "GetCustomerByNameAndGender(string name, Gender gender)", perhaps in CustomerRepository:IRepository<Customer>
- Create an Object Query language as I go that accepts calls maybe with fluid interface calls like .Where(PropertyName name) and so on
- Respect the Single Reponsibility Principle and use NHibernate's API within the Domain, but only within query objects.
The first option really just moves the complexity into different area. Maintaining a million little methods really misses out on the joys of Object Oriented thinking where you can treat tasks as things and have adaptable code. The second option is tempting at first, but this is where the business man in me has to speak up and question the wisdom in this route. Do I really want to write a parallel Object Query language just for the sake of independence from an external tool's API? I think this is called "not created here" (or something like that) syndrome, right?
So, after weighing my options, I very simply refactored my implementations of IQuery<T> to use the rich NHIbernate API and my Domain Entities use these objects that fill a collection of ICriteria objects, IProjection types and so on. MY IRepository<T> accepts any object of type IQuery<T> and returns the results I need. If I ever need to switch to a different OR/M provider I'll have to write implementations of IQuery<T> that speak that ORM's language. Really, if I am making that kind of change to an application, this level of adaption seems acceptable.
One tool that can keep these changes from affecting my service layer is some kind of Dependency Injection container that gets the appropriate instance for me at runtime. So instead of instantiating IQuery<T> implementations directly from, say, a higher service layer, I can invoke Castle or Structure Map or whatever to get the instance that I configured. These kinds of tools are perfect for these situations.
OK ...back to coding.