Какие шаблоны проектирования используешь для backend?
Комментарии (4)
Ответ сгенерирован нейросетью и может содержать ошибки
Основные шаблоны проектирования в backend-разработке на C#
В backend-разработке я использую комбинацию классических шаблонов проектирования (GoF) и современных архитектурных паттернов, адаптированных под специфику серверных приложений. Вот ключевые категории и примеры их применения:
1. Архитектурные паттерны
Эти паттерны формируют структуру всего приложения.
Repository и Unit of Work — фундамент для работы с данными:
public interface IRepository<T> where T : class
{
Task<T> GetByIdAsync(int id);
Task AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
}
public class UnitOfWork : IUnitOfWork
{
private readonly AppDbContext _context;
public async Task CompleteAsync()
{
await _context.SaveChangesAsync();
}
// Паттерн Unit of Work объединяет несколько репозиториев
// в одну транзакционную единицу
}
Dependency Injection (внедрение зависимостей) — основа современного C# backend:
public class UserService : IUserService
{
private readonly IUserRepository _repository;
private readonly ILogger<UserService> _logger;
// Зависимости инжектятся через конструктор
public UserService(IUserRepository repository, ILogger<UserService> logger)
{
_repository = repository;
_logger = logger;
}
}
2. Паттерны предметной области (Domain-Driven Design)
Aggregate Root — инкапсуляция бизнес-логики:
public class Order : AggregateRoot
{
private readonly List<OrderItem> _items = new();
public void AddItem(Product product, int quantity)
{
// Бизнес-правила валидации
if (quantity <= 0)
throw new BusinessException("Количество должно быть положительным");
_items.Add(new OrderItem(product, quantity));
}
// Паттерн гарантирует целостность агрегата
}
Specification — для сложных критериев выборки:
public class ActiveUsersSpecification : Specification<User>
{
public override Expression<Func<User, bool>> ToExpression()
{
return user => user.IsActive &&
user.LastLoginDate > DateTime.UtcNow.AddDays(-30);
}
}
3. Паттерны для обработки запросов
CQRS (Command Query Responsibility Segregation) — разделение операций:
// Команда для изменения состояния
public class CreateUserCommand : IRequest<int>
{
public string Email { get; set; }
public string Name { get; set; }
}
// Запрос только для чтения
public class GetUserQuery : IRequest<UserDto>
{
public int UserId { get; set; }
}
// Использование MediatR для обработки
public class CreateUserHandler : IRequestHandler<CreateUserCommand, int>
{
public async Task<int> Handle(CreateUserCommand request, CancellationToken ct)
{
// Логика создания пользователя
}
}
Mediator — посредник для уменьшения связности:
// Вместо прямых вызовов сервисов
public class UserController : ControllerBase
{
private readonly IMediator _mediator;
[HttpPost]
public async Task<IActionResult> Create(CreateUserCommand command)
{
var result = await _mediator.Send(command);
return Ok(result);
}
}
4. Паттерны для повышения надежности
Circuit Breaker — защита от каскадных сбоев:
public class ResilientHttpClient
{
private readonly CircuitBreakerPolicy _circuitBreaker;
public ResilientHttpClient()
{
_circuitBreaker = Policy
.Handle<HttpRequestException>()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
}
public async Task<string> GetAsync(string url)
{
return await _circuitBreaker.ExecuteAsync(async () =>
{
// Выполнение HTTP-запроса
});
}
}
Retry и Fallback — стратегии обработки временных сбоев:
var policy = Policy
.Handle<TimeoutException>()
.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
.FallbackAsync(fallbackAction: async ct =>
{
// Альтернативная логика при постоянных сбоях
});
5. Паттерны для производительности
Strategy — алгоритмы, выбираемые в runtime:
public interface IPaymentStrategy
{
Task ProcessPaymentAsync(decimal amount);
}
public class CreditCardStrategy : IPaymentStrategy { }
public class PayPalStrategy : IPaymentStrategy { }
public class CryptoStrategy : IPaymentStrategy { }
// Контекст использует стратегию
public class PaymentProcessor
{
private IPaymentStrategy _strategy;
public void SetStrategy(IPaymentStrategy strategy)
{
_strategy = strategy;
}
}
Decorator — динамическое добавление функциональности:
public class CachingUserRepositoryDecorator : IUserRepository
{
private readonly IUserRepository _innerRepository;
private readonly IMemoryCache _cache;
public async Task<User> GetByIdAsync(int id)
{
return await _cache.GetOrCreateAsync($"user_{id}", async entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
return await _innerRepository.GetByIdAsync(id);
});
}
}
6. Паттерны для событийной архитектуры
Observer/Publisher-Subscriber — для loosely coupled систем:
public class DomainEventPublisher
{
private readonly List<IDomainEventHandler> _handlers = new();
public void Publish(DomainEvent @event)
{
foreach (var handler in _handlers)
{
handler.Handle(@event);
}
}
}
Процесс выбора паттернов всегда определяется конкретными требованиями:
- Сложность бизнес-логики → DDD паттерны
- Требования к производительности → CQRS, кэширование
- Надежность и отказоустойчивость → Circuit Breaker, Retry
- Гибкость и тестируемость → Dependency Injection, Repository
Ключевой принцип — не слепое следование паттернам, а их осмысленное применение для решения конкретных проблем: управления сложностью, повышения тестируемости, обеспечения масштабируемости и поддержки эволюции системы. В современных C# проектах особенно важно сочетать классические GoF-паттерны с архитектурными подходами типа Clean Architecture или Vertical Slice Architecture, которые определяют общую структуру приложения.