3

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.

closed as not a real question by Jim G., user40980, gnat, Kilian Foth, Bart van Ingen Schenau May 21 '13 at 14:23

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center. If this question can be reworded to fit the rules in the help center, please edit the question.

  • I may not be following you correctly, but: If you are writing everything into the service layer then the only thing you should be repeating is the device/format specific UI interactions with the services. I am not certain I would want a phone app with ALL of it's business logic on a remote server, but if that is what you are doing then the only thing that sounds weird is that your service is not designed for the lowest common denominator. – Bill May 20 '13 at 14:18
  • @Bill Yes, you are right, I want all my logic in the services, and yes, the different app UI's would interact with the services - but the services need a Unit of Work, and each application type determines when (a new) one is started and disposed, right? – Jeff May 20 '13 at 14:29
  • 2
    "Am I totally crazy for even attempting this?" I think yes. – Euphoric May 20 '13 at 14:35
  • 2
    I'm not understanding why maintaining the one instance is critical. Sounds like your goals would be met by simply emulating a web-app-style service-driven architecture in all cases. Assuming you want a desktop app to work without being online, the thing to change would be the point of interaction between client and "server" and the rest could stay the same. I've never written a desktop app but coming from a web background client/service separation would strike me as a useful pattern to continue following. Service handles biz logic and db interaction. Client provides user interaction. – Erik Reppen May 20 '13 at 16:26
  • @ErikReppen It's critical because if each service has it's own Unit of Work, and one service calls the other, problems will arise since the objects are being tracked by 2 UoW's, which I've heard is not good (EF does not appear to like it). – Jeff May 20 '13 at 17:29
4

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 ;)).

  • Yes, you are absolutely right - the problem is, however, how can I make sure that all my services and all my repositories are working with the same Unit of Work instance? That's the real problem I am facing. – Jeff May 20 '13 at 14:26
  • Maybe I'm missing something, but if you want your business logic and data persistance code in the service layer, why not just make the service layer return some kind of opaque identifier? Say, to begin shopping, you must first call a service method ObtainShoppingCart() which returns an ID which must be passed along to every other method that deals with the user's shopping cart. How to preserve that ID between calls would obviously be a different problem in a desktop application and a web frontend. – a CVn May 20 '13 at 14:33
  • Implementation-simplifying POCOs and helper methods to fill them from service calls could quite conceivably go into some kind of library, thus minimizing code repetition. But some problems will always have to be solved differently depending on the platform in question. – a CVn May 20 '13 at 14:34
  • The problem should be common to any kind of application, unless I am completely stupid. Say my ASP.NET controller takes a ICustomerService as a ctor parameter. The CustomerServiceImpl takes an ICustomerRepository and an IUnitOfWork as a parameter. The CustomerRepositoryImpl needs the same IUnitOfWork instance as the service does, else they would be working on different contexts/sessions. – Jeff May 20 '13 at 14:36
  • My problem is that any application that wants to use ICustomerService needs to provide a Unit of Work and a Repository at the time the Customer Service is constructed. An ASP.NET MVC constructs a new controller for every web request, and the controller wants a customer service, so it is instantiated, all behind the scenes. This magic is not given to us in, say Winforms, right? We'd have to explicitly call our IoC container to build the dependency chain whenever we want to create a sub form. – Jeff May 20 '13 at 14:39
0

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.

  • But if I have 2 services, where 1 uses the other, should they each have their own unit of work? – Jeff May 20 '13 at 16:51
  • In the one service you have an IoC, in the other service you have whatever serialized representation that any other client calling a service would have, they are two different things. Also at this point I am going to point out like the other folks, you are probably making this issue more difficult by trying to define Unit of Work in an unnatural way. – Bill May 21 '13 at 15:32
  • Yes, you're probably right, I just don't know how to do it right. – Jeff May 21 '13 at 18:33
0

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.

  • The reason I need to reuse them across services, is when service1 calls service2. :) – Jeff May 20 '13 at 16:47

Not the answer you're looking for? Browse other questions tagged or ask your own question.