6

I am designing an ASP.NET MVC3 application, and I would like to have a clear separation of concerns in a 3 layer architecture. I am using Fluent NHibernate as the ORM, the Repository pattern to work with the entities mapped by NHibernate. I would like to add a proper business layer with a Unit Of Work pattern, keeping the MVC portion only for presentation (by using ViewModels that map to the nHibernate entities through the business layer). This article describes the combined 3-tier and MVC architectures nicely.

According to this MVC + unit of work + repository article I don't see a clear distinction of a business layer. The unit of work class presents strongly typed getters for each repository type, which looks appropriate for a business layer. However, it exposes a Save method, which I think would translate to BeginTransaction and CommitTransaction methods with nHibernate. This begs some questions:

1) Is exposing transaction control to MVC a good idea? At which stage should transaction control happen? Seems to me that MVC should not be responsible for transactions, but how to avoid that?

2) Should there be some automatic way to handle transactions? This ActionFilter implementation is semi-automatic but the transaction control is clearly in the MVC section, which is not the business layer.

3) Is the UnitOfWork class the same as a business layer class?
- if so, does that mean that we can add custom business logic methods into it?
- if not, do we wrap the unit of work with some other class(es) that contains business logic methods?

I appreciate any ideas or examples. Thank you.

Community
  • 1
  • 1
Alex
  • 9,250
  • 11
  • 70
  • 81

3 Answers3

12

First of all I want to clarify a little mis-conception about the business layer, as you want to use the Repository pattern and your setup is a candidate to Domain Driven Design, then the business layer is actually [the Domain Model (Entities and Value Objects where you design your business logic in an object oriented fashion in entities and objects) , and Application Layer to co-ordinate transactions and operations and commands to the domain layer], so the suggested architecture would be something like this:

  • Presentation (MVC) [OrderView, OrderPresentationModel, OrderController]
  • Application [OrderService]
    • Use UnitOfWork (Transactions) and Repositories to execute domain logic
    • DTOs [OrderDTO, CustomerDTO]
  • Domain
    • Entities and Value Objects [Order, Customer, LineItem, Address]
    • Repository Interfaces [IOrderRepository, ICustomerRepository]
    • (optional) Unit of Work Interface [IUnitOfWork]
  • Infrastructure.DataAccess (Using ORM or Data Access Technology)
    • Repositories [OrderRepository, CustomerRepository]
    • (optional) Unit of Work [UnitOfWork]
  • Infrastructure.Common
    • Logging
    • Utilities

Example scenario:

[Presentation] OrderController:

 _orderService.CreateOrder(OrderDTO);

[Application] OrderService:

 _unitOfWork.BeginTransaction();
 var customer = _customerRepository.GetById(orderDTO.CustomerId);
 var order = new Order() { Customer=customer, Price=orderDTO.Price, ... }
 _orderRepository.Add(order);
 _unitOfWork.Commit();

About your questions:

1) Is exposing transaction control to MVC a good idea? At which stage should transaction control happen? Seems to me that MVC should not be responsible for transactions, but how to avoid that?

No, i would prefer to separate it in application layer in order to make design flexible to support different presentations.

2) Should there be some automatic way to handle transactions? This ActionFilter implementation is semi-automatic but the transaction control is clearly in the MVC section, which is not the business layer.

Use transactions in application layer.

3) Is the UnitOfWork class the same as a business layer class? - if so, does that mean that we can add custom business logic methods into it? - if not, do we wrap the unit of work with some other class(es) that contains business logic methods?

No, it is just a way to group tasks into transactions. The business logic actually is encapsulated in entities and if the logic is not related to one entity it should be implemented in domain services [TransferService.Transfer(account1, account2, amount)], for co-ordinations, accessing repositories, and transactions the application layer is the place.

Mohamed Abed
  • 5,025
  • 22
  • 31
  • 1). Is the _unitOfWork in the [Application] just a thin layer for Nhibernate session? Why not just use session.BeginTransaction directly? Is there some transaction management that you're doing inside unitOfWork? 2). Why are the DTO objects necessary? Can't we just pass the entities that were mapped with NHibernate to the presentation layer? What's different about the DTO from the NHibernate mapped entities? 3). How should we handle the NHibernate session lifetime in this schema? – Alex Oct 16 '11 at 13:31
  • `UnitOfWork is optional as stated, NHibernate's session includes the logic for unit of work and transaction, but it could be abstracted if you want the flexibility of having more than persistence technique. you could also use .Net distributed transaction if you want. – Mohamed Abed Oct 16 '11 at 15:37
  • DTO objects are necessary if your domain objects are complex and it is more customizable to presentation for example in designing domain object Order object it could included many attributes and operations (methods) that have nothing to do with presentation and designed in a complex object oriented way, for presentation usually you need a more flat data objects that is optimized for presentation and binding. – Mohamed Abed Oct 16 '11 at 15:40
  • Alright. So how would you handle NHibernate session lifetime? Since our presentation layer can be MVC or desktop UI, or web service, user session lifetimes can be different. – Alex Oct 17 '11 at 02:24
  • Using IoC container you can customize the lifetime manager for the session based on the client, if it is Web application (MVC) then make it per http request. if it is Web Service also make it per request, if it is windows application then you can make it singleton ... the good thing is that you dont implement this lifecycle management in any of your layers, just configure the IoC container in the client layer – Mohamed Abed Oct 17 '11 at 09:06
  • It looks like someone grab your answer and blogged it as his own work http://fukyo-it.blogspot.com.br/2012/02/aspnet-mvc3-3-tier-design-transaction.html – Fabio May 08 '13 at 17:38
1

Have you looked into the S#arp Architecture? It has built-in MVC Transaction handling, using Action Filters.

I'm usually a layering purist, so I wasn't psyched about letting MVC open the view, but I've come to like it.

There are pro's and con's, but here are some advantages of the Open Session In View pattern.:

  1. Lazy Loading -- It will simplify your data access if you can rely on lazy loading in your MVC Views. No need to explicitly join to all tables required by the view. (Be sure to avoid the N+1 problem by joining explicitly when lazy loading would generate a ridiculous number of queries. Data grids are a common culprit here.)
  2. Simpler BSO layer -- your BSO doesn't need to worry about sessions. It's assumed that you're already working in the context of a session.

If you don't want to go with S#arp, you can still implement Open-Session-In-View yourself with only a few lines of code in an ActionFilter. On the Action Executing method, open the session. In the action executed method, commit, close, and dispose. This is what we're doing on my project and it has worked well for us.

Community
  • 1
  • 1
awright
  • 1,224
  • 1
  • 13
  • 23
  • So, you're advocating handling transactions completely on the MVC side? What are the cons? What did you mean by "I wasn't psyched about letting MVC open the view"? Did you mean "open the session"? – Alex Oct 14 '11 at 20:12
  • Yes, "open the view" was a typo. Some cons, in my experience: 1. Lazy loading can be chatty -- it will use more queries than you might need if you explicitly joined to all the tables you would need. 2. From a pure layering point of view, it's a violation of the Single Responsibility Principle for the presentation to be opening a database transaction. However, in my experience this has not caused problems if your team is senior enough to understand that opening the session is the _only_ database-related task the presentation does. – awright Oct 14 '11 at 23:56
  • So, to summarize, you advise a single session for the life of each http session and single transaction per controller action? – Alex Oct 15 '11 at 03:37
0

The problem you have with any web based app is that there has to be some coupling of the UI to the buisness layer due to the need to manage object lifetimes by the HTTP Session. In a typical desktop application you don't have to worry about sessions, so this makes it easy to move all transaction handling further down the chain.

Consider where you want to reuse the same logic in three apps, a Web Site, a Web Service, and a Desktop application. Without exposing the transaction handling to the presentation tier, there's no good way to deal with transaction commits, beacuse the business layer is ignorant of how long the objects will exist.

So your choice is, either make sure you commit everything in every method of your buisness objects, or expose the transaction to the UI.

A third alternative would be to build a fairly complex session management controller, which I don't even want to think about... The session management controller could be coupled to the ui and the business logic, seperating them to some extent.. But that would require a lot more analysis.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • I think I like committing everything within the business objects, but I'd like to discuss the cons of that approach. What is the trade off? – Alex Oct 15 '11 at 03:40