Lesson 13 of 40
Architecture
Advanced
45 min
Dependency Injection & Architecture Patterns
Design maintainable, testable applications using the built-in DI container, clean architecture, vertical slice architecture, and the mediator pattern.
Part 1: Service Lifetimes
| Lifetime | When to Use | Register |
|---|---|---|
| Transient | Lightweight, stateless services | AddTransient |
| Scoped | Per-request state (e.g., DbContext) | AddScoped |
| Singleton | Shared app-wide state (e.g., cache) | AddSingleton |
Part 2: Keyed Services
// Register multiple implementations
builder.Services.AddKeyedSingleton<IPaymentGateway, StripeGateway>("stripe");
builder.Services.AddKeyedSingleton<IPaymentGateway, PayPalGateway>("paypal");
// Inject by key
public CheckoutService([FromKeyedServices("stripe")] IPaymentGateway gateway)
builder.Services.AddKeyedSingleton<IPaymentGateway, StripeGateway>("stripe");
builder.Services.AddKeyedSingleton<IPaymentGateway, PayPalGateway>("paypal");
// Inject by key
public CheckoutService([FromKeyedServices("stripe")] IPaymentGateway gateway)
Part 3: MediatR & CQRS
Separate commands (write) from queries (read):
public record CreateOrderCommand(OrderDto Dto) : IRequest<Order>;
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, Order>
{
public async Task<Order> Handle(CreateOrderCommand cmd, CancellationToken ct)
{
// Business logic here
}
}
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, Order>
{
public async Task<Order> Handle(CreateOrderCommand cmd, CancellationToken ct)
{
// Business logic here
}
}
Part 4: Decorator Pattern via DI
// Add caching decorator without modifying original
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.Decorate<IOrderRepository, CachedOrderRepository>();
// Scrutor NuGet package provides .Decorate()
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.Decorate<IOrderRepository, CachedOrderRepository>();
// Scrutor NuGet package provides .Decorate()