Какие архитектурные подходы вы применяете при разработке бэкенд-сервисов и почему?

Ответ

При разработке я придерживаюсь принципов, которые делают систему гибкой, тестируемой и простой в поддержке. В основном это Чистая архитектура (Clean Architecture) или её вариант — Гексагональная архитектура (Ports & Adapters).

Почему именно эти подходы?

  1. Независимость от фреймворков и внешних сервисов: Бизнес-логика (домен) не зависит от конкретной базы данных, веб-фреймворка или системы очередей. Это позволяет легко заменять компоненты.
  2. Тестируемость: Ядро системы (use cases и доменные сущности) можно тестировать изолированно, без поднятия веб-сервера или подключения к БД.
  3. Четкое разделение ответственности: Код организован по слоям, что делает его понятнее.

Типичная структура проекта:

/internal
  /app          # Слой приложения (Use Cases), координирует домен и инфраструктуру
  /domain       # Бизнес-сущности, агрегаты и основная логика. Ни от чего не зависит.
  /handler      # Адаптеры для входящих запросов (HTTP, gRPC, CLI)
  /repository   # Адаптеры для работы с хранилищами (PostgreSQL, Redis)
/pkg            # Общий код, который можно использовать в других проектах
/cmd            # Точки входа в приложение (main.go)

Выбор технологий в зависимости от задачи:

  • API:

    • REST (с OpenAPI): Для публичных API, где важна простота и широкая поддержка.
    • gRPC: Для высокопроизводительного межсервисного взаимодействия внутри системы. Protobuf обеспечивает строгую типизацию и обратную совместимость.
    • GraphQL: Когда клиенту (часто фронтенду) нужна гибкость в получении данных, чтобы избежать множества запросов (over-fetching/under-fetching).

  • Веб-фреймворки: Предпочитаю минималистичные и быстрые фреймворки, такие как Chi или Echo, так как они не навязывают свою структуру и хорошо вписываются в Чистую архитектуру.


  • Продвинутые паттерны:

    • CQRS (Command Query Responsibility Segregation): Применяю в системах, где модели для чтения и записи сильно различаются. Это позволяет оптимизировать каждую операцию независимо.
    • Event Sourcing: Использую, когда важна полная история изменений состояния системы (аудит, отладка) или для построения сложных асинхронных процессов.