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.
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:
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.
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.:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With