For the past week I have been attempting to write a proof-of-concept project using dependency injection, a service layer, unit of work pattern + repository.
I am looking to design something that can easily be consumed by any type of application – be it a Windows desktop application, a CLI application, an ASP.NET MVC application, even an iOS application running Mono. Please, bear in mind that this is just for a proof-of-concept.
Until now, I have written
- A Unit of Work + Unit of Work Factory for NHibernate and Entity Framework (2 OR/M frameworks for .NET)
- (Generic) repository. There is one repository per OR/M implementation, taking it’s respective Unit of Work Factory as a constructor parameter. It needs this to grab the current unit of work (since it’s implementation contains stuff necessary for each OR/M framework to function)
This has accomplished:
- I can swap out implementations of the repository and Unit of Work factory with NHibernate and Entity Framework, and it will just work. This is good, except I haven’t implemented a real application yet, only unit-tests.
These are the problems/challenges I am facing:
Each application type (Desktop, CLI, ASP.NET MVC, iOS, Android, whatever) do not define a unit of work the same way. A desktop application (AFAIK) should have a Unit of Work per screen – a Web application should have a Unit of Work per request, etc. I don’t know how I would share a single unit of work with all the repositories/services (see below) that needs it.– Michael is right.- I want all my logic to reside in a service layer. The service layer decides when a Unit of Work should commit. The idea is that whoever calls the service, knows what to pass to it, and knows what to expect as a result. If something goes wrong, the service logs it. If I had to do this on a per-application basis, DRY would be violated (right?).
It seems that if I had to implement this totally separated design, the amount of code I got to reuse would not be as much as the amount of code I’d have to write in order to implement it in each application type, which would, in the end, most likely leak my IoC container into the application.
Am I totally crazy for even attempting this?
EDIT: The actual problem I am facing, is providing my service layer and repositories with the same instance of a Unit of Work, no matter what application type is being used.
10
Each application type (Desktop, CLI, ASP.NET MVC, iOS, Android, whatever) do not define a unit of work the same way. A desktop application (AFAIK) should have a Unit of Work per screen – a Web application should have a Unit of Work per request, etc. I don’t know how I would share a single unit of work with all the repositories/services (see below) that needs it.
To me, this sounds like you are defining “a unit of work” around how it is presented to the user, rather than around what makes sense to perform as a single operation. Depending on how much state you want to keep and where, “a unit of work” isn’t going to be “purchase an item” (taking an example for an ecommerce application just to have a concrete example) — that’s more like a scenario or use case — but perhaps rather “add given quantity of given product to shopping cart”, “get contents of shopping cart”, “set delivery address”, “set payment details” and “confirm order”.
If you do it that way, you can easily expose the functionality (purchasing something) in ways that make sense in whatever UI you are working with. A web application might display the contents of a shopping cart on one page and ask for customer details on another, while a client GUI application might integrate it all into one physical view. Each client application will of course still require knowledge of how to perform the various operations, but they won’t need intimate knowledge of the overall architecture any more than you need to know every implementation detail of Win32 to build Windows applications (oops, bad example ;)).
6
It sounds like you are either doing something extremely stateful in your services or something very odd in the client.
EF context or NHibernate equiv are not going to be an easy thing to maintain across requests, once you get to load balancing and such it becomes next to impossible to do correctly, so you end up passing a token or using session (which is really a token lookup anyway) and the instances do not matter as much because you end up with methods like:
AddItemToCart(CartID, ItemID)
that are simple. You IoC the providers and abstract with a manager/controller/service. Even if you pass the whole object across the service it goes through a serializer or something on both ends and you have two or more instances anyway.
3
I’m struggling to see why you need to reuse units of work across service calls. Loading a shopping cart is a unit of work. Saving changes to that shopping cart is a separate unit of work.
Perhaps the real problem is in your definition of what comprises a unit of work. Whenever you call a service method in the application API, that is, by definition, a unit of work. If your units of work are breaking across multiple calls, that suggests to me that you have a business logic leak into your UI code.
1