4

I'm a bit confused about the relationship a BLL and DAL has. Should the BLL encapsulate the DAL via dependancy injection? Or should the BLL only act on domain objects and the DAL save/update seperately?

For example imagine (in a typical MVC app) a Cancel Order function that requires you to update the order and update the stock. Would the following be how my action would look?

public ActionResult CancelOrder (Guid orderId) {
    Order order = orderRepository.Get(orderId);
    StockItem stockItem = stockRepository.Get(order.StockItemId);

    _orderService.CancelOrder(order, stockItem);
    orderRepository.Update(order);
    orderRepository.Update(stock);
    Return View();
}

Or should it be more like the following?

public ActionResult CancelOrder (Guid orderId) {
    _orderService.CancelOrder(orderId);
    Return View();
}

(within OrderService)
public void CancelOrder(Guid orderId) {
    Order order = orderRepository.Get(orderId);
    StockItem stockItem = stockRepository.Get(order.StockItemId);

    order.Cancelled = true;
    stockItem.AmountInStock = stockItem.AmountInStock + order.Amount;
    orderRepository.Update(order);
    orderRepository.Update(stock);
}

With this option everything would then be handled by the BLL including data access. The repositories would be injected in to avoid tight coupling. Any entity retrieval would then take the form of _orderService.GetOrder(orderId); as apposed to going straight to the repository.

Excuse the crudeness of the examples as I don't have a lot of time. Does any of what I've written even remotely make sense or am I off in the wilderness?

David
  • 1,731
  • 24
  • 37

4 Answers4

5

Definitely not the first option, which embeds your business logic in the controller. The problem is not that the controller accesses data objects per se, but that a procedure dictated by business rules has to be followed. This procedure has no place in the controller.

So you should either go with the second option, or possibly make Cancel a method of Order. If you have already written similar code just go with consistency.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • Well the second option was my preference. About placing a Cancel method within the Order class, would this perform actions against the DB? It's been drummed into me via my career that domain objects shouldn't care about data access. Or do you mean something else? – David Sep 05 '12 at 10:16
  • @David: No, that's what I mean. There are designs where domain objects have knowledge about the data store they belong to. If that's not you, ignore. :) – Jon Sep 05 '12 at 10:17
  • 1
    +1. I tend to put logic which involves multiple entities in the service to keep the persistence (repositories) out of the entities. – jgauffin Sep 05 '12 at 10:20
1

Think of separation of concerns, Controller is from MVC pattern which is PRESENTATION pattern, so controller should contains presentation logic support presentation layer, not business logic.

It is agreed that business logic should be in domain entities, but there are also some APPLICATION logic which plays a roles as coordinators between repositories, that is why the service layer is downed on the road.

Therefore, option 2 should be in your way.

cuongle
  • 74,024
  • 28
  • 151
  • 206
1

You're really asking 2 questions here :

What should be in the Controller vs in the business layer ?

=> I tend to think the code in your first snippet is the right level of responsibility for an Application layer service (and consequently for a Controller if you admit that the two can be likened, which there's a lot of discussion about these times). Getting the Order from the repository and saving it after the cancel operation hardly seems like pure business logic. It has more to do with the surrounding transaction/unit of work and the plumbing of your use case.

I'd just change one thing - try to save changes to all entities affected by your transaction in one go. If you have to manually update every entity that could possibly be changed at the end of an operation, it's going to be a big pain and will pollute the controllers unnecessarily. Create a unit of work system (or use an existing implementation) that will persist all changes at once and remove all the Update() methods in your repositories.

Other than that, as Jon suggests, I also believe that a rich Order domain object containing the Cancel() method would be preferrable to a service - but this is another debate.

What kind of relationship should there be between BLL and DAL ?

=> The BLL shouldn't be tightly coupled to the DAL and as the centermost layer, it isn't supposed to reference outer layers directly. This way you can easily reuse your BLL in another application, with another DAL, etc.

However, there are times when some business objects need direct access to other objects they don't already have a reference to, which basically means getting them from the database. In other words, some operations in the BLL need to talk to the repositories. Therefore, I always place repository interfaces in the BLL, but their implementations reside in the DAL, and they are injected into the BLL at runtime.

As a result, the BLL is only loosely coupled to the DAL. It remains persistence ignorant in that it manipulates only facades (repositories) which look like neutral collections of objects and stays oblivious of how data is stored, hydrated, and so on.

guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • I think I am definately favouring a lightly coupled BLL and DAL. I think to keep myself honest the question I ask is "how easy would this be to port to a desktop app?" and with option 1 business rules are starting to creep into what is effectively the front end. I'd like to get to the stage where my MVC app is just a thin client consumer of the service layer. Agreed with Transactional comment, using NHibernate a lot I wrap everything in Transactions as recommended. – David Sep 05 '12 at 15:37
  • "business rules are starting to creep into what is effectively the front end" - I agree, but these are *application-specific* business rules, not *domain-specific* business rules. The rule that cancelling an Order results in a stock increase by the corresponding quantity is domain-specific, it will never change whatever the application. In contrast, what tells you that in a desktop or mobile app, the Order needs to be saved right after it is cancelled (application-specific) ? What tells you the order and timing of the operations performed won't change, if the use case is slightly different ? – guillaume31 Sep 05 '12 at 15:55
  • So, you may be right wanting your Controllers to be a thin layer with only UI concerns and devoid of application business rules. But IMO the better solution is to create an additional Application services layer rather than placing these application-specific business rules directly in the domain (or BLL). – guillaume31 Sep 05 '12 at 16:04
0

BLL should act on the business objects that you have created for your application. It, ideally, should not be aware of database and related operations. If you are looking to keep things loosely coupled, make use of dependency injection to call methods from your DAL.

danish
  • 5,550
  • 2
  • 25
  • 28