Какая была архитектура микросервиса который создавал?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Архитектура микросервиса для системы обработки финансовых транзакций
Я разрабатывал микросервис для обработки онлайн-платежей в финтех— проекте. Архитектура была многослойной (multi-layered) с чётким разделением ответственности и ориентацией на event- driven взаимодействие.
Основные компоненты архитектуры
// Пример структуры проекта в .NET
PaymentService/
├── PaymentService.API/ // Контроллеры, DTO, middleware
├── PaymentService.Application/ // Use Cases, Services, Validators
├── PaymentService.Domain/ // Сущности, агрегаты, доменные события
├── PaymentService.Infrastructure // Репозитории, внешние клиенты, БД
└── PaymentService.Background/ // Фоновые задачи, consumers
1. Коммуникация и взаимодействие
Микросервис использовал гибридный подход к коммуникации:
- REST API (синхронный): Для клиентских инициирующих запросов (создание платежа, проверка статуса). Использовал
**ASP.NET Core Web API** с **Swagger** для документации.
- Event-driven (асинхронный): Основной способ взаимодействия с другими сервисами (оркестрация платежного
процесса). Использовал **RabbitMQ** (сначала) и позже **Kafka** для **повышения отказоустойчивости** и обработки
пиковых нагрузок.
// Пример контроллера для синхронного API
[ApiController]
[Route("api/payments")]
public class PaymentController : ControllerBase
{
private readonly IPaymentService _paymentService;
[HttpPost]
public async Task<ActionResult<PaymentResponse>> CreatePayment([FromBody] CreatePaymentRequest request)
{
var result = await _paymentService.ProcessPaymentAsync(request);
return Ok(result);
}
}
// Пример подписчика на доменное событие (асинхронно)
public class PaymentCompletedEventHandler : INotificationHandler<PaymentCompletedEvent>
{
private readonly IEventPublisher _publisher;
public async Task Handle(PaymentCompletedEvent notification, CancellationToken ct)
{
await _publisher.PublishAsync(new PaymentCompletedIntegrationEvent(notification.PaymentId));
}
}
2. Внутренняя структура и паттерны
- Чистая архитектура / Ports & Adapters: Ядро сервиса (Domain и Application слои) не имело зависимостей от
инфраструктурных деталей.
* **Domain**: Содержал агрегат `Payment` с инвариантами (например, нельзя завершить неуспешный платеж), **value
objects** (`Money`, `PaymentMethod`), **domain events** (`PaymentInitiatedEvent`,
`PaymentCompletedEvent`).
* **Application**: Содержал **CQRS** (Command Query Responsibility Segregation) для разделения операций записи и
чтения. Использовал **MediatR** для обработки команд и запросов.
* **Infrastructure**: Реализовывал интерфейсы из Application слоя: **Repository pattern** с Entity Framework Core
для `IPaymentRepository`, клиенты для внешних **Payment Gateways** (Stripe, банковские API), отправщики
событий в брокер.
// Пример команды и обработчика по CQRS
public record ProcessPaymentCommand : IRequest<PaymentResult>
{
public Guid OrderId { get; init; }
public decimal Amount { get; init; }
}
public class ProcessPaymentCommandHandler : IRequestHandler<ProcessPaymentCommand, PaymentResult>
{
private readonly IPaymentRepository _repository;
private readonly IBankGatewayClient _gateway;
public async Task<PaymentResult> Handle(ProcessPaymentCommand cmd, CancellationToken ct)
{
var payment = Payment.Create(cmd.OrderId, cmd.Amount);
await _repository.AddAsync(payment, ct);
var gatewayResponse = await _gateway.ChargeAsync(payment, ct);
payment.Complete(gatewayResponse.TransactionId);
await _repository.UpdateAsync(payment, ct);
// Domain event PaymentCompletedEvent публикуется внутри агрегата
return new PaymentResult(payment.Id, payment.Status);
}
}
3. Критичные инфраструктурные решения
- База данных: PostgreSQL для основного хранилища, Redis для кэширования частых запросов (например,
статусов платежей) и хранения idempotency keys для обеспечения **идемпотентности**.
- Обработка фоновых задач: Hangfire для отложенных и повторяющихся задач (например, сверка с банком,
"проталкивание" зависших платежей).
- Работа с внешними системами: Использовал Polly для реализации стратегий Retry и Circuit Breaker
при вызовах ненадёжных банковских API.
- Конфигурация и secrets: Hashicorp Vault (позже перешли на Azure Key Vault) для хранения чувствительных
данных (API-ключей, строк подключения).
- Мониторинг и логирование: Интеграция с OpenTelemetry для трассировки, Serilog для структурированных
логов в **ELK-стек** (Elasticsearch, Logstash, Kibana), метрики в **Prometheus/Grafana**.
4. Ключевые нефункциональные требования и их реализация
- Масштабируемость: Сервис был статиeless (за исключением сессии БД), что позволяло легко масштабировать его
горизонтально. Consumers событий работали в конкурирующем режиме.
- Отказоустойчивость: Комбинация Circuit Breaker, повторных попыток, dead letter queues (DLQ) в RabbitMQ
для изоляции проблемных сообщений и гарантированной доставки.
- Согласованность: Применяли Saga pattern (на основе событий) для управления распределёнными транзакциями
между сервисом платежей, сервисом заказов и сервисом уведомлений. Каждый шаг саги инициировался событием.
- Безопасность: Все endpoints защищались JWT. Авторизация на уровне запросов с помощью Policy-based
authorization в ASP.NET Core. Все исходящие вызовы к банкам использовали **mTLS**.
Выводы и lessons learned
Эта архитектура показала себя устойчивой в production под высокой нагрузкой (десятки тысяч транзакций в час). Основные проблемы, с которыми столкнулись:
- Сложность отладки распределённого workflow из-за событийной модели – решили внедрением **корреляционных
идентификаторов** и централизованного трейсинга.
- Гарантия exactly-once обработки в событийной системе – комбинировали идемпотентные обработчики на нашей
стороне и поддержку идемпотентности на стороне брокера (Kafka).
- Управление контрактами (DTO, события) между сервисами – внедрили Shared Kernel в виде NuGet-пакетки только
с моделями событий и константами, позже рассматривали **Schema Registry**.
Архитектура эволюционировала от более простой (REST + БД) к событийно-ориентированной, что позволило значительно повысить связность (cohesion) внутри сервиса и уменьшить зацепление (coupling) между сервисами, сделав систему в целом более устойчивой к отказам отдельных компонентов и легче масштабируемой.