JavaRush /Курсы /Модуль 5. Spring /Лекция 169: Как правильно декомпозировать приложения: зон...

Лекция 169: Как правильно декомпозировать приложения: зоны ответственности микросервисов

Модуль 5. Spring
17 уровень , 8 лекция
Открыта

Декомпозиция — это процесс разбиения большой системы (например, монолита) на более мелкие, независимые компоненты (в нашем случае — микросервисы). Эти компоненты должны быть самостоятельными, управляемыми и обслуживаемыми отдельно друг от друга. Однако всё не так просто: разделить приложение на микросервисы — это не то же самое, что разрезать пирог на равные куски. Здесь нельзя действовать наугад: важно учитывать бизнес-логику, границы контекста и минимизацию связей между сервисами.

Если вы когда-нибудь пытались собрать IKEA-шкаф без инструкции, то примерно понимаете, какого масштаба хаос может возникнуть при неправильной декомпозиции. Мы должны быть уверены, что каждая часть системы имеет четкую зону ответственности и взаимодействует с другими компонентами минимально, чтобы не превратиться в "распределённый монолит".


Принципы декомпозиции

  1. Разделение по бизнес-функциям, а не техническим компонентам. Один из самых частых антипаттернов при переходе на микросервисы — попытка декомпозировать приложение по слоям (UI, бизнес-логика, база данных). Это умножает зависимость между сервисами и создаёт дополнительные проблемы при их независимом развертывании. Например, вместо того чтобы создавать один сервис для всех данных, связанный с клиентами, и другой для всех данных о заказах, лучше создать отдельные сервисы для управления клиентами (Customer Service) и заказами (Order Service). Каждый из них будет самостоятельно обрабатывать свои данные, а взаимодействие между сервисами сведётся к минимуму.
  2. Избегайте "богатых" (fat) микросервисов. Иногда соблазнительно свалить всю связанную логику в один микросервис. Но тогда такой сервис становится сложно масштабируемым и поддерживаемым, превращаясь в монолит внутри микросервисной архитектуры. Это как с чемоданом без ручки: и нести тяжело, и бросить жалко.
  3. Слабая связанность (Loose Coupling). Микросервисы должны быть максимально независимыми друг от друга. Любое взаимодействие между ними должно быть минимизировано и формализовано через интерфейсы, такие как API или события. Это упрощает тестирование, обновление и масштабирование отдельных компонентов.
  4. Высокая когезия (High Cohesion). Код в одном микросервисе должен быть связан общими целями. Это как с друзьями: с одной компанией классно пойти в кино, а с другой — обсудить микросервисы. А вот смешивать их не стоит, кто-то обязательно будет скучать.

Определение границ микросервисов

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

Пример:

  • Клиенты (Customer Service)
  • Заказы (Order Service)
  • Платежи (Payment Service)
  • Уведомления (Notification Service)

Так может быть устроен, например, интернет-магазин. Вместо одного монолита, который делает всё (управляет клиентами, заказами, платежами, уведомлениями), мы создаём отдельные микросервисы, каждый из которых отвечает за конкретную бизнес-потребность.


Как избежать распространённых антипаттернов?

  1. Анархическая декомпозиция. Когда нет стратегии, разработчики просто создают микросервисы "как получится". Проблема в том, что это приводит к распределённым монолитам, где системы слишком тесно связаны и появляются сложности с масштабированием.
  2. Дублирование данных. Дублирование данных между сервисами — неизбежно, но его нужно минимизировать. Например, сервис заказов не должен хранить полные данные клиента, достаточно ID клиента, чтобы запросить данные у Customer Service.
  3. Чрезмерная детализация. Не стоит разрабатывать слишком мелкие микросервисы. Если вы пытаетесь сделать отдельный сервис для каждого действия (например, отдельный сервис для "добавить товар в корзину"), это вызовет огромные накладные расходы на коммуникацию.

Практическое упражнение: декомпозиция интернет-магазина

Предположим, вы разрабатываете большой интернет-магазин. Как разделить его на микросервисы?

1. Определяем основные области (Business Domains):

  • Управление клиентами (Customer Management)
  • Управление каталогом товаров (Product Catalog)
  • Управление заказами (Order Management)
  • Обработка платежей (Payment Processing)
  • Управление уведомлениями (Notifications)

2. Разделяем данные: Каждый микросервис должен быть владельцем своих данных:

  • Customer Service хранит информацию о клиентах (имя, email, историю заказов и т.д.).
  • Product Catalog Service управляет информацией о товарах (название, описание, цена, наличие на складе).
  • Order Service обрабатывает заказы (сведения о заказах, статусы).
  • Payment Service отвечает за обработку транзакций (оплата, возвраты).
  • Notification Service отправляет уведомления (email, SMS).

3. Определяем взаимодействие:

  • Customer Service передаёт информацию о клиентах в Order Service.
  • Order Service запрашивает данные о товарах из Product Catalog Service.
  • Order Service передаёт запросы на оплату в Payment Service.
  • Payment Service передаёт данные об успешных платежах в Order Service.
  • Все события могут триггерить уведомления через Notification Service.

4. Выбираем подходы к взаимодействию

  • Для синхронного взаимодействия используем REST API. Для асинхронного — например, Kafka: cобытия, такие как "новый заказ создан" или "платёж прошёл успешно", могут публиковаться в Kafka, чтобы их могли обрабатывать другие микросервисы.

Шаги для декомпозиции монолита в микросервисы

  1. Анализ текущей системы
    • Попытайтесь выделить основные бизнес-функции вашего монолита.
  2. Определите зоны ответственности
    • Объедините связанные функциональные модули в единую область ответственности.
  3. Разделите базы данных
    • Распределите данные между сервисами так, чтобы каждый сервис управлял своей базой данных.
  4. Обеспечьте взаимодействие
    • Выберите между синхронным (REST) или асинхронным (Kafka) взаимодействием.
  5. Миграция функциональности
    • Постепенно переносите функциональность из монолита в микросервисы.
  6. Тестирование
    • Убедитесь, что новые модули работают корректно и взаимодействуют друг с другом.

Пример: REST API между сервисами

Для взаимодействия между Order Service и Product Catalog Service:


// REST-клиент Order Service для получения данных о товарах
@RestController
@RequestMapping("/order")
public class OrderController {

    private final RestTemplate restTemplate;

    public OrderController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @PostMapping
    public ResponseEntity<String> createOrder(@RequestBody OrderRequest orderRequest) {
        // Отправка запроса в Product Catalog Service
        ResponseEntity<Product> productResponse = restTemplate.getForEntity(
            "http://product-service/product/" + orderRequest.getProductId(),
            Product.class
        );

        if (productResponse.getStatusCode().is2xxSuccessful()) {
            // Обработка заказа
            return ResponseEntity.ok("Order created successfully!");
        } else {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Product not found");
        }
    }
}

Для асинхронной передачи событий используйте Kafka:


// Публикация события "Order Created" в Kafka
@Service
public class OrderEventPublisher {

    private final KafkaTemplate<String, String> kafkaTemplate;

    public OrderEventPublisher(KafkaTemplate<String, String> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    public void publishOrderCreatedEvent(Order order) {
        kafkaTemplate.send("orders", order.toString());
    }
}

Итоги

Правильная декомпозиция требует глубокого понимания ваших бизнес-процессов, работы системы и возможных узких мест. Сервис должен быть достаточно крупным, чтобы выполнять осмысленную задачу, но достаточно простым для разработки, тестирования и деплоя. Держите в голове основной принцип: один микросервис — одна бизнес-функция.

Подобно слаженной работе кухни ресторана, где каждый повар отвечает за свой участок работы, микросервисы независимо выполняют свои задачи, но вместе создают целостную систему.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ