Wednesday, June 18, 2008 -
I've heard/read rumblings over the last few months that "SubSonic is tightly coupled" and therefore you have to "drag it around" with you in your project. I can see why people might think this - ActiveRecord is not the most testable thing in the world :). I've really tried to push SubSonic into the TDD realm and thought it might be a good idea to show how you can structure up a highly testable, decoupled application using SubSonic as your Data Access tool.
The Repository Pattern
This is usually the first choice in Data Access patterns when it comes to writing testable, decoupled code. So I'm going to use that today. If you arent' familiar with it, you may want to read more here. Essentially, the idea here is that you want to abstract your data access/query bits as much as possible from the rest of your application - including your model. I go into this a lot in the MVC Storefront series.
Rather than dive into explanations here, I'll just show the code :).
Solution Setup
I'm using ASP.NET MVC - but you don't have to use this. You can use whatever application setup you like. I'm dividing out the data access and test projects as well, so that I have 4 total in my application:
Data Access
Let's assume, for now, that I'm writing tests for everything I do. And my tests tell me that I need to setup a Catalog Repository that talks to my DB (for today it will be Northwind). My tests tell me I need to have a Product so the first thing I do is setup a class to represent a Product:
public class Product { public int ProductID { get; set; } public string ProductName { get; set; } public decimal UnitPrice { get; set; } }
Next, I need to have a method on my Repository that will retrieve products from the DB. For those of you following along with the MVC Storefront, I'm going to use the classic Repository Pattern here, without using IQueryable.
I define an interface that will abstract the implementation of the Repository:
public interface ICatalogRepository { IList<Product> GetProducts(); }
Finally, I need to setup an implementation of this interface, and it will use SubSonic. I reference and setup SubSonic in the Data Access class, and then generate all the good stuff SubSonic will create for me here. Note that I can use ActiveRecord or RepositoryRecord - it doesn't matter, and that's just how I want it. All I'm going to use is the new Query tool - none of the base objects that are created (except for the Repository querying).
After generating the code, my project looks like this:
Note: you'll notice an App.config in the project. This is ONLY to tell SubCommander how/where to run the generation. You don't need to do this (you can send everything in to SubCommander by command line)- I just find it handy.
In the "Generated" folder are all the classes SubSonic generates for you to use and include a full schema look at your DB. This will allow us to query as we need to.
Mapping
If you're not use to using ORM tool (Object-Relational Mappers), you may not know why I want to "duplicate" the classes that SubSonic generates, versus the one I created here in my "Model" folder.
The reason comes down to what's known as "impedance mismatch" - the idea that my DB structure will not follow my application structure (and therefore OO principles). Moreover, if you tie your DB structure to your application, eventually you will end up with some pain points as your DB grows - and to mitigate these you compromise good DB practices. The opposite is true as well - you "bend" your application to allow for easier use of the DB. I'll end this discussion here :).
What I need to do now is implement ICatalogRepository using SubSonic, and map the Northwind Products to my Model. This is easy to do with SubSonic, using "ExecuteTypedList<>":
public class SubSonicCatalogRepository:ICatalogRepository { public IList<Product> GetProducts() { return Northwind.DB.Select("ProductID", "ProductName", "UnitPrice") .From<Northwind.Product>().ExecuteTypedList<Product>(); } }
ExecuteTypedList<> tries to match the names of the returned columns to the names of the properties of the passed-in type. In this example they match exactly - and that's not completely real world. You can get around this by aliasing the columns - in the same way you'd alias a SQL call:
return Northwind.DB.Select("ProductID as 'ID'", "ProductName as 'Name'", "UnitPrice as 'Price'") .From<Northwind.Product>().ExecuteTypedList<Product>();
Note: Using "as" is ANSI SQL, and will work with most DB providers - including MySQL 5.x and above.
You can also Stored Procedures here, or Views - up to you.
The Test
In the Test Application, all you need to do is:
I'm looking into hard-coding the connection string info (like Linq To Sql does) so you can omit the config stuff - but I feel weird about that. Would love to hear some feedback...
This is my Test Project:
Note that I don't have any reference to SubSonic here :). I don't need it - it's abstracted nicely away. Now I can write my tests, stubbing out ICatalogRepository with a TestCatalogRepository, and use Dependency Injection to inject the SubSonicCatalogRepository as needed :).
To make sure that SubSonic is playing nicely here, you can write an integration test:
[TestMethod]
public void SubSonic_Should_Return_Product_List() {
ICatalogRepository rep = new SubSonicCatalogRepository();
IList<Product> products = rep.GetProducts();
Assert.AreEqual(77, products.Count);
}
And here's the result:
Yes, I know I shouldn't hard-code Count values, but this is a sample :).
Putting It All Together
The one thing I haven't shown yet is my Business Logic bits. The one thing you don't want to do in your application is instantiate a Repository class directly - that's "coupling" and ties your application to the implementation.
To get around this, you can use Dependency Injection to "inject" the Repository into your business logic class at runtime. If you want to know more about "why" you'd do this, I did a nice Dependency Injection screencast with Jeremy Miller (creator of StructureMap) on the subject.
Normally, your Service classes should implement your business logic so your Repository only concerns itself with getting the data you want. An example of this is that you may grab a list of Products and want to check inventory levels, whether the shopper is eligible for discounts - etc. You'll probably want to keep this type of logic in the application tier:
public class CatalogService { ICatalogRepository _repository; public CatalogService(ICatalogRepository repository) { _repository = repository; } public IList<Product> GetProducts() { List<Product> products = _repository.GetProducts(); //inventory checks //discount setups //cross-sell correlation //bundling... } }
The main thing about this setup is that the CatalogService class is FORCED to take a Repository in the constructor - this is exactly what we want in order to make our code more testable and decoupled.
Normally you might put some Linq To Sql code in here, or perhaps straight-up ADO stuff with an IDataReader. That may be a great call with a smaller app, but with anything that you want to maintain over the next few years, you're better off trying to keep it cleanly separated like this.
An example of why you might want this decoupling is you might decide to move your application to .NET 3.5 in the future and may want to use SubSonic 3.0, or Linq To Sql (or NHib... whatever). It's trivial to swap the parts out if you follow this architectural pattern.
Dependency Injection
It may seem like a bummer to have to instantiate all these things just to get at a list of Products, but that's where DI comes in. If we plug in a DI tool (like StructureMap), we can rely on it to do the associations we need:
ForRequestedType<ICatalogRepository>()
.TheDefaultIsConcreteType<SubSonicCatalogRepository>();
Setting this up is really, really easy and if you're interested in learning more about DI, take a look at my screencast with Jeremy Miller.
I'm very interested to hear your comments. This setup is pretty tried and true, and if you'd like to see more about how to do this type of thing, I'd push you to check out the MVC Storefront. I'm not using SubSonic there (yet) because it doesn't support IQueryable - but the ideas are pretty much the same.
Great post Rob. I've been lurking the SubSonic project for some time now and have to say that I love it for its simplicity and ease of use. However, more than once the active record pattern has caused major headaches in my domain forcing me to compromise the design of either my code base or my data base (never an easy choice).
Coincidentally enough, the solution you've outlined here (creating your domain then mapping the SubSonic auto-generated classes to it) is actually one I had been pondering about this week.
In the past, whenever I encountered a project where I knew I would have large differences between my domain structure and database structure, I would go with NHibernate. However, I never did like having all those XML files hanging around (as well as having to deal with setting up the .config files and session / factory objects). This solution seems like a much cleaner and easier way to give you full control over your domain objects, your mappings, as well as your database. Essentially, it moves what is normally handled inside the NHibernate XML files to the code base, which has a couple advantages:
1. Not having to learn how to code up NHibernate xml
2. Strong typed database columns
Once again, good work. And I look forward to seeing SubSonic 3.0. :)
Nice Rob, what about generating an Interface for the table columns so that you can pass them around Interfaces instead
@Yitzchok: You can do that, but it doesn't really get you anywhere with this mapping technique. Tell me more about what you have in mind here...
Rob, Any chance you can throw the sample code up as a zip file?
@Rob I first just skimmed the post next time I have to really read it first sorry :|, now I understand
and I can say "Very" Nice
Your right this really doesn't have anything to do with this mapping technique.
What I was think was more like what to do if you want to "use the generated classes" but don't want a reference to SubSonic in the UI layer. (Plain classes that just have properties you don't really use Interfaces but here its (I think) the only way around)
I debated for awhile whether or not to finish a post I was in the middle of writing that touched on this very same topic because I didn't want to seem like I was ripping off your ideas and claiming them as my own. Eventually, I figured it couldn't hurt and may end up providing more insight to people interested in using SubSonic and the repository pattern.
You can check out my post here:
lypang.com/.../SubSonic-and-th
I'd be interested in hearing your thoughts.
@Rob
Nice post on testability. I have one question on Repository Pattern and it'd be great if you can answer.
1. Should we create IXRepository per class or per logical unit?
2. How to manage deep save from the IXRepository. What I mean, suppose in your ICatalogRepository you have a method void Save(Catalog). If you created new Catalog object and added several products to this catalog, what is best practice for deep save within RepositoryPattern?
Hi Rob,
You may want to credit Chris Cyvas over @ http://www.dashcommerce.org for coming up with the crux of this solution - IoC / DI - as he was the source of the "rumblings" AND the source of the solution.
Please be fair and give credit where it is due. Otherwise, good post.
What about the ODS controllers generated by SubSonic? Do they fit in here somewhere, or do you recommend not generating them unless you specifically want/need them for an ODS?
@Frans: With all due respect, I've been using this exact pattern on the storefront for months now, and moreover the Repository Pattern itself is widely used. I don't know what Chris came up with (short of using IoC) - but I doubt it looks anything like this.
Yes, I'm aware of his grumbling.
@Alex: ODS Controllers still work the way they normally would - but I generally don't use them.
@Rob,
I would like to second Scott and request the sample code as a zip if at all possible.
Thanks for the great work.
Rob,
Great post. SubSonic has always made it so easy for me to write data access code that I've just let it seep into areas it doesn't belong (i.e. code-behind). I've never slowed down enough to think about the best way to truly decouple it the way it should be. This is certainly an approach I'll be looking at for any new projects.
My blog entry about this post:
www.mpaladino.com/.../Default.aspx
Hi Rob,
This is off topic..but I can't get to any of the Sonicasts. I get the message:
Your request could not be completed.
Either the site is offline or an error occurred. Please try your request again or if you know who your site administrator is let them know too.
Sorry for any inconvenience this may have caused you.
Thanks.
Hi Rob,
I appreciate this post immensely. I was going to ask how to implement IoC using your new repository pattern as the DB is used statically. You demonstrated this beautifully and in turn also demonstrated exactly why this is such a fantastic and empowering design architecture. As a project moves forward, it is simple to add horizontal aspects such as caching and security to the data access layer without dirtying up the objects or data access code. You simply implement the iRepository interface in a new higher level class and pass through to the original repository, incrementing the required aspect on top of that result. As long as there is a factory method to retrieve an instance of iRepoistory, the future develop has a single place to intercept these calls and add funtionality as needed. Thanks so very much!