Ответ
Архитектура следовала принципам предметно-ориентированного проектирования (DDD), обеспечивая слабую связность и высокую степень автономности сервисов.
Основные характеристики:
- Декомпозиция по доменам: Сервисы соответствовали бизнес-доменам (
user-management,order-processing,payment-service). - Автономность: Каждый сервис владел своей схемой БД (использовались PostgreSQL, MongoDB) и развертывался независимо.
- Стили коммуникации:
- Синхронная: REST API и gRPC для прямых запросов-ответов.
- Асинхронная: Брокер сообщений RabbitMQ (позже Kafka) для событий (
user.registered,order.placed), обеспечивая слабую связность.
- Инфраструктура: Оркестрация через Kubernetes с Helm-чартами, service mesh (Istio) для управления трафиком и безопасности.
- Наблюдаемость: Единый стек: Prometheus (метрики), Grafana (дашборды), Jaeger (трассировка), Loki (логи).
Пример автономного теста сервиса (pytest):
# Тест изолирован от других сервисов благодаря мокам
def test_create_user_unit(user_service, mock_db_session, mocker):
"""Тест бизнес-логики создания пользователя."""
# Мок внешней зависимости (например, сервиса нотификаций)
mock_notify = mocker.patch("services.notification_client.send")
new_user_data = {"email": "test@example.com", "name": "Test"}
result = user_service.create_user(new_user_data)
assert result.id is not None
assert result.email == new_user_data["email"]
# Проверка, что побочный эффект (нотификация) был вызван
mock_notify.assert_called_once()
Сложности и решения: Для обеспечения согласованности данных в распределенных транзакциях применялся Паттерн Сага (Saga Pattern). Отладка упрощалась за счет сквозных идентификаторов запросов (correlation IDs) в логах и трассировках.
Ответ 18+ 🔞
А, слушай, вот это архитектура была — просто песня, если честно. Не то что эти ваши монолиты, где всё в одной куче, как говно в проруби. Мы тут забили на эту срань и пошли по пути предметно-ориентированного проектирования, это DDD, если по-умному. Суть в чём? Чтобы всё было слабо связано, а сервисы жили своей жизнью, как соседи по коммуналке — вроде рядом, но каждый сам по себе и в душу друг другу не лезет.
Что у нас там было по полочкам:
- Декомпозиция по доменам: Каждый сервис — это кусок бизнеса.
user-management— там, юзеров плодить,order-processing— заказы мутить,payment-service— бабки стричь. Чётко, ясно, без этой ебалы «а чьё это вообще?». - Автономность полная: Каждый сервис — царь и бог своей базы данных. У кого PostgreSQL, у кого MongoDB. Разворачивать можно по одному, не надо всю эту махину, как утюг, поднимать.
- Как они общались:
- Синхронно: Когда срочно надо — REST API или gRPC (это такая жёсткая штука, быстрее и чётче).
- Асинхронно: А когда можно не торопиться — через брокер сообщений. Сначала RabbitMQ, потом на Kafka переползли. Кидаешь событие типа
user.registeredилиorder.placedи спишь спокойно. Кто надо — тот подхватит. Связность — ниже плинтуса, красота.
- Инфраструктура: Всё на Kubernetes болталось, управлялось Helm-чартами. А для тонких штук вроде маршрутизации или безопасности — Istio (service mesh, если кому интересно).
- Наблюдаемость: Чтобы не гадать, что там внутри происходит, был полный стек: Prometheus метрики собирал, Grafana красивые графики рисовала, Jaeger за трассировкой следил, а Loki логи в кучу собирал. В общем, видно всё, как на ладони, а не как в тумане.
Вот, глянь, как автономно тесты писались (pytest):
# Тест изолирован от других сервисов благодаря мокам
def test_create_user_unit(user_service, mock_db_session, mocker):
"""Тест бизнес-логики создания пользователя."""
# Мок внешней зависимости (например, сервиса нотификаций)
mock_notify = mocker.patch("services.notification_client.send")
new_user_data = {"email": "test@example.com", "name": "Test"}
result = user_service.create_user(new_user_data)
assert result.id is not None
assert result.email == new_user_data["email"]
# Проверка, что побочный эффект (нотификация) был вызван
mock_notify.assert_called_once()
Видишь? Сервис в вакууме. Никаких реальных вызовов куда-то на сторону. Всё замокано. Быстро, надёжно, не зависит ни от кого.
Сложности и решения: Ну а куда без них, блядь. Самая главная головная боль — как в этой раздолбанной системе данные согласовывать. Ты в одном сервисе обновил, а в другом — хуй. Для этого Паттерн Сага (Saga Pattern) применяли. Это когда длинная транзакция разбивается на цепочку маленьких, и если где-то пиздец, то остальные шаги откатываются. А чтобы не сойти с ума при отладке, везде прокидывали сквозные идентификаторы запросов (correlation IDs). Откроешь Jaeger или Loki, вбиваешь этот ID — и тебе вся история, как по ниточке, от начала и до конца, даже если запрос через десять сервисов пролетел. Без этого — просто тьма египетская, волнение ебать.