← Назад к вопросам

Какая была архитектура микросервиса который создавал?

3.0 Senior🔥 121 комментариев
#Архитектура и микросервисы

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектура микросервиса для системы обработки финансовых транзакций

Я разрабатывал микросервис для обработки онлайн-платежей в финтех— проекте. Архитектура была многослойной (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 под высокой нагрузкой (десятки тысяч транзакций в час). Основные проблемы, с которыми столкнулись:

  1. Сложность отладки распределённого workflow из-за событийной модели – решили внедрением **корреляционных
    идентификаторов** и централизованного трейсинга.
  1. Гарантия exactly-once обработки в событийной системе – комбинировали идемпотентные обработчики на нашей
    стороне и поддержку идемпотентности на стороне брокера (Kafka).
  1. Управление контрактами (DTO, события) между сервисами – внедрили Shared Kernel в виде NuGet-пакетки только
    с моделями событий и константами, позже рассматривали **Schema Registry**.

Архитектура эволюционировала от более простой (REST + БД) к событийно-ориентированной, что позволило значительно повысить связность (cohesion) внутри сервиса и уменьшить зацепление (coupling) между сервисами, сделав систему в целом более устойчивой к отказам отдельных компонентов и легче масштабируемой.