Pregunta

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?

¿Fue útil?

Solución

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.

Otros consejos

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.

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.

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top