Ответ
Выбор паттернов зависит от масштаба и сложности приложения. Вот ключевые, которые я применяю на практике:
-
Repository & Unit of Work. Абстрагируют доступ к данным, что упрощает тестирование и смену источника данных (например, с EF Core на Dapper).
public interface IRepository<T> where T : class { Task<T?> GetByIdAsync(int id); void Add(T entity); void Remove(T entity); } public interface IUnitOfWork : IDisposable { IRepository<User> Users { get; } IRepository<Order> Orders { get; } Task<int> SaveChangesAsync(CancellationToken ct = default); } -
Dependency Injection (Внедрение зависимостей). Фундаментальный паттерн в ASP.NET Core для управления зависимостями и повышения тестируемости.
// Регистрация в Startup.cs / Program.cs services.AddScoped<IUserService, UserService>(); services.AddSingleton<ICacheService, RedisCacheService>(); -
CQRS (Command Query Responsibility Segregation). Разделение моделей для операций записи (Commands) и чтения (Queries). Особенно полезен в высоконагруженных системах, где требования к чтению и записи разные.
- Command: Изменяет состояние системы (
CreateOrderCommand). - Query: Возвращает данные без изменения состояния (
GetUserOrdersQuery). Часто реализуется с помощью библиотеки MediatR.
- Command: Изменяет состояние системы (
-
Mediator. Позволяет уменьшить связанность между компонентами. Компоненты общаются не напрямую, а через посредника.
// С MediatR public record CreateUserCommand(string Email, string Name) : IRequest<int>; public class CreateUserHandler : IRequestHandler<CreateUserCommand, int> { private readonly AppDbContext _context; public CreateUserHandler(AppDbContext context) => _context = context; public async Task<int> Handle(CreateUserCommand request, CancellationToken ct) { var user = new User { Email = request.Email, Name = request.Name }; _context.Users.Add(user); await _context.SaveChangesAsync(ct); return user.Id; } } -
Strategy. Используется, когда необходимо выбирать алгоритм во время выполнения.
public interface IPaymentStrategy { Task<PaymentResult> ProcessAsync(decimal amount); } public class PaymentProcessor { private readonly IPaymentStrategy _strategy; public PaymentProcessor(IPaymentStrategy strategy) => _strategy = strategy; public Task<PaymentResult> ExecutePayment(decimal amount) => _strategy.ProcessAsync(amount); } // Внедряем конкретную стратегию: CreditCardStrategy, PayPalStrategy и т.д. -
Decorator. Для динамического добавления поведения объекту (логирование, кэширование, валидация) без изменения его класса.
public class LoggingUserServiceDecorator : IUserService { private readonly IUserService _innerService; private readonly ILogger _logger; public LoggingUserServiceDecorator(IUserService innerService, ILogger logger) { _innerService = innerService; _logger = logger; } public async Task<User> GetUserAsync(int id) { _logger.LogInformation("Fetching user {UserId}", id); return await _innerService.GetUserAsync(id); } }
Для простого CRUD-приложения часто достаточно Repository + DI. Для сложных, масштабируемых систем добавляются CQRS, Mediator и стратегии.