Aller au contenu principal
Nicolas Cousin Tech SolutionsNicolas Cousin Tech Solutions
Module 5 of 6

Practical Example: E-commerce Platform

Let's take an example of an order management application in an e-commerce platform. Here is how the Repository and Unit of Work patterns can be used together:

Scenario: Creating an Order

  1. An order contains information about the customer, the items ordered, and the total amount.
  2. The operation must:
    • Create a new order.
    • Update the inventory for the items involved.
    • Record the payment transaction.

Implementation Code

public class OrderService
{
    private readonly IUnitOfWork _unitOfWork;

    public OrderService(IUnitOfWork unitOfWork)
    {
        // Inject the UnitOfWork to coordinate transactions
        _unitOfWork = unitOfWork;
    }

    public async Task<bool> CreateOrderAsync(Order order, IEnumerable<OrderItem> orderItems)
    {
        try
        {
            // Step 1: Add the order
            await _unitOfWork.Repository<Order>().AddAsync(order);

            // Step 2: Update inventory for each ordered item
            foreach (var item in orderItems)
            {
                var product = await _unitOfWork.Repository<Product>().GetByIdAsync(item.ProductId);
                if (product == null || product.Stock < item.Quantity)
                {
                    throw new InvalidOperationException("Insufficient stock.");
                }

                product.Stock -= item.Quantity; // Decrease stock
                _unitOfWork.Repository<Product>().Update(product);
            }

            // Step 3: Record the payment transaction
            var payment = new Payment
            {
                OrderId = order.Id,
                Amount = orderItems.Sum(i => i.Price * i.Quantity),
                PaymentDate = DateTime.UtcNow
            };
            await _unitOfWork.Repository<Payment>().AddAsync(payment);

            // Step 4: Save all changes in a single transaction
            await _unitOfWork.SaveChangesAsync();

            return true; // Returns true if everything succeeded
        }
        catch (Exception)
        {
            // Error handling: the transaction will be rolled back on exception
            throw;
        }
    }
}

Explanations

  • Logic Isolation: Each repository handles a specific entity (Order, Product, Payment).
  • Single Transaction: All operations are grouped via the Unit of Work, ensuring data consistency.
  • Ease of Maintenance: Application logic remains clear and well-structured.

Check your understanding

In the order-creation scenario, what happens if stock is insufficient?

Why call SaveChangesAsync() only once at the end of the method?