Wednesday, September 22, 2010

N-Tiers using POCOs and Entity Framework - Part Four: Business Layer

Edit: Code updated

The business layer will receive the POCO from the presentation layer and it will execute all the business logic applicable, calling the data access layer when it is necessary.

BusinessLayer

In order to communicate both tiers, the business layer exposes Application Services implemented using Windows Communication Foundation (WCF). These Application Services shouldn’t have any business logic; they have the responsibility to expose business operations to the presentation layer. The only two things that they should do are to call the business domain and to coordinate the Unit of Work pattern in order to persist all entities’ changes in a centralized point per WCF request.

namespace Business.Application.Services
{
  public class SalesModuleAppService : ISalesModuleAppService
  {
    private readonly IShopDomainService shopDomainService;
    private readonly IUnitOfWork unitOfWork;

    public SalesModuleAppService()
    {
        shopDomainService = // Get IShopDomainService from IoC Container
        unitOfWork = // Get IUnitOfWork from IoC Container
    }

    public void ProcessOrder(Order order)
    {
      shopDomainService.ConfirmOrder(order);

      unitOfWork.Save();
    }
    ...
  }
}

Note: Since this example is about updating an existing Order, if the Application Service receives a DTO instead of the Entity, you should retrieve the original Order and then set its properties using the values of the DTO.


As we can see, the Application Service is calling the ConfirmOrder() method of the Domain Service in order to execute the business logic. This method has the responsibility to coordinate the cancellation of the old order and the confirmation of the new one. In order to do that, it has to obtain the already persisted Order and cancel it, then to apply the new values in the ORM context (using the OrderRepository) and finally to execute the confirmation logic of the new Order.

namespace Business.Domain.Services
{
  public class ShopDomainService : IShopDomainService
  {
    private readonly IOrderRepository orderRepository;

    public ShopDomainService(IOrderRepository orderRepository)
    {
      this.orderRepository = orderRepository;
    }

    public void ConfirmOrder(Order order)
    {
      if (!order.IsNew())
      {
        var originalOrder = orderRepository.Get(order.Id);
        originalOrder.Cancel();
      }

      var currentOrder = order.IsNew() 
        ? orderRepository.Insert(order) 
        : orderRepository.Update(order);

      currentOrder.Confirm();
    }
    ...
  }
}

Note: Domain Services (ShopDomainService) have different purpose than Application Services (SalesModuleAppService). While the first ones are business-oriented, providing stateless business operations, the second ones act as boundary to the business layer, exposing its operations to other tiers.


In the following code snippets we can see a partial implementation of the extension methods applicable to the Order and the Product Entities, containing their corresponding business logic.

namespace Business.Domain.Entities
{
  public static class OrderExtensions
  {
    public static void Confirm(this Order order)
    {
      // Here run the business rules 
      // (e.g. to verify the customer's status)

      order.Product.SubstractStock(order.Quantity);
      ...
    }

    public static void Cancel(this Order order)
    {
      order.Product.RestoreStock(order.Quantity);
      ...
    }
    ...
  }
}
namespace Business.Domain.Entities
{
  public static class ProductExtensions
  {
    public static void SubstractStock(this Product product, int quantity)
    {  
      if(quantity > product.Stock)
        throw new InvalidOperationException("Insufficient stock");

      product.Stock -= quantity;
      ...
    }

    public static void RestoreStock(this Product product, int quantity)
    {
      product.Stock += quantity;
      ...
    }
    ...
  }
}

References:

  • Evans, DDD: Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software, Boston, MA: Addison-Wesley 2004.

Posts in this series:

  1. Architecture
  2. Model and Entities
  3. Presentation Layer
  4. Business Layer
  5. DataAccess Layer
  6. Source Code

No comments: