Модульный монолит, паттерны взаимодействия между модулями

Модульный монолит, паттерны взаимодействия между модулями

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

Главный принцип: Модуль НЕ ДОЛЖЕН знать о внутренней реализации другого модуля! Все взаимодействие — ТОЛЬКО через строго определенные публичные интерфейсы (контракты).

Существует несколько подходов коммуникации между модулями. Есть варианты синхронного и асинхронного взаимодействия. Разберем несколько из них.

Синхронные вызовы

В этом варианте нам необходим синхронный вызов одного модуля другим. Здесь есть множество вариаций организации такой связи. Вызов метода API напрямую и использование подхода "Ports and Adapters"

Вызов API другого модуля напрямую

Это самый простой вариант взаимодействия между модулями - модуль напрямую вызывает API другого через интерфейс:

Модульный монолит, паттерны взаимодействия между модулями

Этот вариант прост в реализации и не требует дополнительных обвязок и/или библиотек/фреймворков.

Преимущество такого подхода:

  • Скорость вызова
  • Легко реализовать
  • Нет направленности связей

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

Использование подхода Port/Adapter

Этот вариант отличается от предыдущего дополнительным слоем абстракции. Который позволяет абстрагироваться от вызываемого модуля. В этом случае модуль вызывает абстрактный класс/интерфейс, а непосредственный вызов производится на уровне реализации порта - адаптер.

Модульный монолит, паттерны взаимодействия между модулями

Помимо преимуществ, которые мы получаем при прямом вызове метода API, мы получаем еще:

  • Легко менять реализацию (что будет огромным плюсом при преобразовании в микросервис).
  • Упрощается тестирование - можно реализовывать моки на любой вкус.

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

Асинхронные вызовы

В этом случае взаимодействие между модулями полностью асинхронное. Модуль публикует событие в шину событий внутри процесса. Модули, заинтересованные в этом событии, подписываются на него и асинхронно обрабатывают его когда смогут. Модуль публиковавший события не знает и не ждет реакции подписчиков. Например, можно реализовать отправку и обработку событий с помощью Spring Events

@Service public class OrderService { private final ApplicationEventPublisher eventPublisher; public OrderService(ApplicationEventPublisher eventPublisher) { this.eventPublisher = eventPublisher; } public void createOrder(Order order) { // ... создание заказа ... eventPublisher.publishEvent(new OrderCreatedEvent(order)); } } @Component public class OrderCreatedEventListener { @EventListener public void handleOrderCreatedEvent(OrderCreatedEvent event) { // ... обработка события ... } }

Преимущество такого подхода:

  • Четкое разделение ответственности - вызывающий знает какое событие нужно послать, обработчик знает как обработать это событие.
  • Возможность асинхронного выполнения - не блокировать вызывающего.

При использовании данного подхода также можно воспользоваться подходом Ports and Adapters, что позволит абстрагироваться от механизма передачи сообщения и в дальнейшем вместо spring events использовать брокеры/очереди сообщений, например ZeroMQ или тот же RabbitMQ. Стоит учитывать, что передача результатов выполнения команд, также потребует дополнительных механизмов.

Итог

Правильное взаимодействие между модулями — это основа модульного монолита. Используйте:

  • Синхронные вызовы для критичных операций.
  • События для асинхронной коммуникации.
  • Порты и адаптеры для изоляции внешних зависимостей.

Мой канал в telegram

1 комментарий