Как вы разделяли микросервисы на прошлом месте работы?

Ответ

Мы использовали подход, основанный на Domain-Driven Design (DDD) и принципах bounded context. Основные шаги:

1. Выявление доменов и контекстов:

  • Провели event storming-сессии с бизнес-аналитиками и разработчиками.
  • Выделили core domains (например, Order, Payment, Delivery, Catalog).
  • Определили границы контекстов, чтобы минимизировать связи между ними.

2. Критерии разделения на сервисы:

  • Бизнес-возможность: Каждый сервис отвечал за одну четкую бизнес-функцию (например, обработка платежей).
  • Независимое развертывание и масштабирование: Сервис Notification мог масштабироваться отдельно от Order в период распродаж.
  • Владение данными: У каждого сервиса была своя приватная база данных. Обмен данными шел только через публичные API или события.

3. Реализация коммуникации:

  • Синхронная (REST/gRPC): Для операций, требующих немедленного ответа (например, проверка наличия товара).
  • Асинхронная (Event-Driven через Kafka): Для обеспечения слабой связанности и надежности. Пример события:
    
    // Общий контракт события в отдельной библиотеке
    public class OrderPlacedEvent
    {
    public Guid OrderId { get; set; }
    public decimal TotalAmount { get; set; }
    public string CustomerEmail { get; set; }
    }

// Сервис Payment подписывался на это событие для запуска процессинга.



**4. Инфраструктурные решения:**
*   **Контракты:** DTO и события выносились в общие NuGet-пакеты.
*   **Мониторинг:** Централизованный сбор логов (ELK Stack) и метрик (Prometheus/Grafana) для каждого сервиса.
*   **Обнаружение сервисов:** Использовали Consul для динамической маршрутизации.

Главный урок: разделение должно быть по бизнес-границам, а не по техническим слоям (например, не делать отдельный сервис "База данных" или "Логика").

Ответ 18+ 🔞

Да ты посмотри, какая красота! Мы тут, блядь, целую архитектуру выстроили, как пить дать — по канонам Domain-Driven Design (DDD). Не просто так сервисы нарезали, а с мозгом, чтобы потом не пришлось переделывать всё на коленке.

1. Выявление доменов и контекстов: Собрались мы, значит, с бизнес-аналитиками и разработчиками, устроили мозговой штурм, который они event storming обозвали. Выделили ключевые домены — Order, Payment, Delivery, Catalog. Главное было — границы контекстов нарисовать так, чтобы они друг за друга не цеплялись, как пьяные в подворотне. Чтобы каждый жил своей жизнью, понимаешь?

2. Критерии разделения на сервисы: Тут мы не стали мудрить, а взяли простые и понятные правила:

  • Одна бизнес-возможность — один сервис. Платежи — отдельно, заказы — отдельно. Не надо из одного сервиса делать швейцарский нож, который и колбасу режет, и гвозди забивает.
  • Независимое развертывание и масштабирование. Это же, блядь, основная фишка микросервисов! Чтобы, когда распродажа, сервис уведомлений Notification можно было масштабировать в овердохуища раз, а сервис заказов Order при этом не трогать.
  • Владение данными. Каждый сервис — царь и бог в своей базе. Никакого прямого доступа к чужим таблицам! Общаемся только через API или события. Иначе получится такая каша, что мама не горюй.

3. Реализация коммуникации: А вот тут уже интереснее. Мы не стали пихать всё в один стиль.

  • Синхронная (REST/gRPC): Использовали, когда нужен был мгновенный ответ. Ну, например, пользователь добавляет товар в корзину — надо тут же проверить, есть ли он на складе. Ждать событий из Kafka тут было бы идиотизмом.
  • Асинхронная (Event-Driven через Kafka): А это наше всё для слабой связанности. Сервис заказов создал заказ — отправил событие OrderPlacedEvent в топик и забыл. А уже сервис платежей Payment, доставки Delivery и нотификаций Notification сами подписались и делают что надо. Красота!
// Контракт события лежал в общем NuGet-пакете, чтобы все одинаково понимали
public class OrderPlacedEvent
{
    public Guid OrderId { get; set; }
    public decimal TotalAmount { get; set; }
    public string CustomerEmail { get; set; }
}
// Сервис Payment, получив такое событие, начинал процессинг платежа. Всё чётко.

4. Инфраструктурные решения: Без нормальной инфраструктуры это всё — просто куча говна, которая развалится при первой же нагрузке.

  • Контракты (DTO, события) — вынесли в общие библиотеки, чтобы не было рассинхрона. Иначе один сервис ждёт поле TotalSum, а другой шлёт TotalAmount — и пиши пропало.
  • Мониторинг. Подняли централизованный сбор логов (ELK Stack) и метрик (Prometheus/Grafana). Чтобы когда что-то ебнулось, не бегать по десяти серверам с фонариком, а сразу видеть, в каком сервисе пиздец.
  • Обнаружение сервисов. Взяли Consul, чтобы сервисы друг друга находили динамически, без жёстких прописанных IP-адресов в конфигах.

И главный вывод, который мы сделали, — разделять нужно строго по бизнес-границам, а не по техническим слоям. Нельзя делать отдельный сервис "для всей логики" или, того хуже, "для всей базы данных". Это будет не микросервисная архитектура, а монолит, распиленный на куски болгаркой — бесполезно и опасно.