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?

Want to hear about the next modules?

The Academy stays free and open-access, no sign-up required. If you'd just like to be notified by email when a new module ships, here you go — no obligation, unsubscribe anytime with one click.