Repository and Unit of Work Pattern - Practical Example: E-commerce Platform
Case Study: Discover the joint usage of the two patterns to manage orders, inventory, and payments in an e-commerce platform.
Summary
This article is part of a series covering the Repository and Unit of Work patterns:
- General Introduction and Usefulness in Enterprise Projects
- Details of the Repository Pattern
- Details of the Unit of Work Pattern
- Compatibility and Limitations with Microservices
- Practical Example: Joint Usage in an E-commerce Platform
- Conclusion and Lessons Learned
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
- An order contains information about the customer, the items ordered, and the total amount.
- 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)
{
// Injection de l'UnitOfWork pour coordonner les transactions
_unitOfWork = unitOfWork;
}
public async Task<bool> CreateOrderAsync(Order order, IEnumerable<OrderItem> orderItems)
{
try
{
// Étape 1 : Ajouter la commande
await _unitOfWork.Repository<Order>().AddAsync(order);
// Étape 2 : Mettre à jour l'inventaire pour chaque article commandé
foreach (var item in orderItems)
{
var product = await _unitOfWork.Repository<Product>().GetByIdAsync(item.ProductId);
if (product == null || product.Stock < item.Quantity)
{
throw new InvalidOperationException("Stock insuffisant.");
}
product.Stock -= item.Quantity; // Réduction du stock
_unitOfWork.Repository<Product>().Update(product);
}
// Étape 3 : Enregistrer la transaction de paiement
var payment = new Payment
{
OrderId = order.Id,
Amount = orderItems.Sum(i => i.Price * i.Quantity),
PaymentDate = DateTime.UtcNow
};
await _unitOfWork.Repository<Payment>().AddAsync(payment);
// Étape 4 : Sauvegarder toutes les modifications dans une transaction unique
await _unitOfWork.SaveChangesAsync();
return true; // Retourne vrai si tout s'est bien passé
}
catch (Exception)
{
// Gestion des erreurs : la transaction sera annulée en cas d'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.