Let me preface this by saying that I am pretty biased towards
SubSonic. When I first stumbled across the open source project it was the first ORM that I had ever seen, and to this day I think it's the
greatest thing since sliced bread. "Having strong typed access to my database without having to deal with .xsd files and datasets? The dickens you say!" It was love at first sight. It didn't hurt that it took all of 2 seconds to set up. So you can imagine my pain when I discovered that although SubSonic was extremely cool and useful for rapid development (the scaffolding controls alone saved me days of development), it had its shortcomings in more complex projects where the database and domain are less one-to-one-y (I touched upon the reason why in my last post on the S#arp Architecture project).
Now, let me also preface this by saying that Rob Conery beat me to the punch on this topic with his recent blog post, which contains many of the same concepts I'm about to discuss. Rest assured that what follows are my own thoughts and that I am not in the business of stealing other people's ideas off and claiming them for myself. I'm aware that this design may have already been discussed to death by other developers, but if so I wasn't aware of it. Regardless, it's always nice to be validated, and I couldn't help but feel a little proud of myself for coming up with an implementation with one that Rob Conery, the creator of the best thing since sliced bread, has decided to use.
The crux of Rob's post (or at least the part that I thought was new and interesting), and the design that I'm going to discuss, is the use of SubSonic within the repository pattern. Traditionally, SubSonic is discussed in the context of the active record pattern, and rightfully so. The auto-generated classes it provides have CRUD functionality built into them and they are meant to be used within the domain. I realize that the latest release of SubSonic introduced some repository pattern-ish features where the auto-generated classes could be configured to not contain the CRUD functionality, but it seemed like this change was geared more towards those deelopers interested in using a data broker implementation, not a true repository pattern implementation. In other words, we were still left dealing with the auto-generated classes SubSonic created which leads to ugliness when the database structure and domain structure stop playing nice with one another.
Now, traditionally whenever this happened, I would have to choose from one of the following options:
- Sacrafice my database design and make the database resemble my domain
- Sacrafice my domain design and make the domain classes resemble the database
- Get frustrated, scrap SubSonic altogether, and use a tool built for domain driven design like NHibernate
For me, the choice was almost always 3 (mainly because I don't like my tools to dictate my design), and for the longest time I thought that was how it would always be. I would simply have to use SubSonic for small, simple projects and NHibernate for large, complex projects. My reasoning was pretty straightforward: SubSonic auto-generates domain classes based on your database structure. So, unless your domain structure is similar to your database structure, you're hosed.
That was the point I was at recently (which is what led me to the S#arp Architecture project, actually) before it dawned on me that there may be another way. What if I kept SubSonic's auto-generated classes but didn't use them in my domain layer? What if instead, I created my domain classes the way I wanted, then used SubSonic's auto-generated classes in a custom ORM that would map my domain classes to my database? In other words, use SubSonic's auto-generated classes as a strong-typed version of my database that I could access in code and write the ORM bits myself instead of relying on the auto-generated classes to perform the ORM functionality.
This implementation has a few things going for it:
- My domain classes and database table structures are completely independent of one another. So long impedence mismatch!
- The ORM logic becomes very transparent and customizable. Whereas with NHibernate, the ORM logic is somewhat hidden behind cryptic .hbm.xml files, this implementation forces me to specify exactly how I want my domain objects mapped to my database (and vis versa)
- Everything is strong-typed. NHibernate .hbm.xml files can get out of sync with your database fairly easily, especially when major refactorings are being done to either the domain or the database. With SubSonic, I get compile time verification that my changes are valid!
- Dependency injection is very easily supported now that we have a true repository that maps the domain to / from the database. With SubSonic's active record approach, the classes would always read / write directly from / to the database, making it very tightly coupled and tough to unit test.
Sure, this forces you to duplicate a lot of the code the auto-generated classes provide. And for simple projects where the auto-generated classes are good enough, this implementation is overkill. But from personal experience, nearly all projects that require a database inevitably seem to grow to the point where SubSonic and the active record pattern simply don't cut it (at least without having to duplicate code left and right). Personally, I think the pros significantly outweigh the cons here. I plan on implementing a sample project sometime soon using the design patterns described here and will post it up as soon as I am done for people to play around with.