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

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

Открыта

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

Если вы когда-нибудь пытались собрать 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());
    }
}

Итоги

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

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

Комментарии
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
У этой страницы еще нет ни одного комментария