Анализ роли шаблона Mvc в интеграции архитектуры микросервисов

Введение: устойчивая актуальность MVC в современных распределенных системах

Модель-View-Controller (MVC) была основополагающей архитектурной концепцией в разработке программного обеспечения в течение десятилетий. Первоначально популяризированная Smalltalk-80 и позже принятая веб-фреймворками, такими как Ruby on Rails, Spring MVC и ASP.NET MVC, основной принцип шаблона - разделение проблем - оказался вневременным. По мере того, как индустрия переходит от монолитных приложений к архитектурам микросервисов, возникает естественный вопрос: есть ли у MVC все еще место в мире независимо развертываемых услуг, событийной связи и децентрализованного управления данными?

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

В конце вы получите более четкое представление о том, как использовать сильные стороны MVC, соблюдая требования к автономности и масштабируемости микросервисов. Для фундаментального понимания микросервисов обратитесь к основополагающей статье Мартина Фаулера об архитектуре микросервисов .

Модель MVC: быстрый рефресер

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

  • Модель — Содержит структуры данных и бизнес-логику, определяющие домен приложения.В традиционной настройке модель часто представляет собой единую схему базы данных с ассоциированными классами объектно-реляционного отображения (ОРМ).Модель уведомляет о взгляде на изменения через шаблон наблюдателя или через общее состояние.
  • View — Обрабатывает уровень представления. Он переводит данные модели в пользовательский интерфейс, обычно веб-страницу или мобильный экран. Вид подписывается на обновления модели и соответственно переоформляется. В современных интерфейсных фреймворках представления являются реактивными компонентами, которые управляют своим собственным состоянием.
  • Контроллер — Обрабатывает входящий пользовательский ввод (HTTP-запросы, представления форм, клики). Он интерпретирует ввод, взаимодействует с моделью для выполнения операций и выбирает соответствующий вид для отображения ответа. Контроллеры — это клей, который координирует поток данных между моделью и видом.

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

Однако в архитектуре микросервисов границы меняются. Каждый микросервис владеет своими данными и логикой, а пользовательский интерфейс часто строится как отдельное фронтенд-приложение, которое взаимодействует с несколькими сервисами. Это вызывает вопрос: как применять MVC, когда нет единого приложения для разделения?

Отображение MVC в микросервисах: Распределенный взгляд

Естественная реакция заключается в том, чтобы рассматривать каждый микросервис как свое собственное приложение MVC. Это правильный подход для определенных сценариев, особенно для служб, которые непосредственно выставляют интерфейс, ориентированный на пользователя (хотя это редко встречается в микросервисах). Чаще всего микросервисы выставляют API, а интерфейс является отдельным потребителем. В этом контексте компоненты MVC распределяются по различным архитектурным слоям.

Модели как сервисные данные

В монолитном MVC-приложении модель распределена по всей кодовой базе. В микросервисах модель децентрализована. Каждая услуга является единственным владельцем своего домена данных. Например, Служба заказа владеет моделью заказа (включая элементы заказа, статус и платежные реквизиты), в то время как Служба клиентов владеет моделью профиля клиента. Это выравнивание с Domain-Driven Design (DDD) означает, что модель не является глобальным уровнем данных, а является агрегатом, специфичным для сервиса.

Следствием этого является то, что не существует единого «источника истины» для всех данных. Услуги обмениваются данными через API или события для синхронизации состояния. Это требует тщательного проектирования для поддержания согласованности данных, часто используя такие шаблоны, как сага-оркестрация или поиск событий. Для более глубокого изучения управления данными в микросервисах см. План поиска событий на microservices.io.

Контроллеры как API шлюзы и конечные точки обслуживания

В классическом MVC контроллер получает запрос и решает, что делать. В микросервисах эквивалентную роль играют шлюзы API и собственные контроллеры конечных точек сервиса. шлюз API действует как единая точка входа для запросов клиентов, маршрутизируя их к соответствующим службам, агрегируя ответы и обрабатывая сквозные проблемы, такие как аутентификация и ограничение скорости. Внутри каждой микросервиса небольшой уровень контроллера интерпретирует входящие запросы (HTTP, gRPC или сообщение) и вызывает бизнес-логику сервиса (модель).

Это разделение означает, что ответственность «контроллера» разделена между шлюзом (который обрабатывает оркестровку и маршрутизацию) и службой (которая обрабатывает логику домена). Это естественное расширение MVC: уровень контроллера остается интерфейсом между пользовательским вводом и операциями домена, но теперь он распределен по всей инфраструктуре.

Обсуждение Frontend Micro Frontends

Представление в среде микросервисов почти всегда является клиентским приложением. Это приложение может быть построено с использованием шаблонов MVC (например, React с Redux или Angular с услугами), но это внешний потребитель. Альтернативно, представление может быть разложено на micro frontends — независимо развернутые фронтенд-фрагменты, каждый из которых принадлежит к определенной группе обслуживания. Это согласуется с принципом микросервисов автономных команд, владеющих как бэкэндом, так и фронтендом для их домена.

Например, поиск продукта может быть микро-фронтендом, принадлежащим команде службы каталогов, в то время как контроль принадлежит команде службы заказов. Каждая часть отображает свой собственный пользовательский интерфейс и взаимодействует с соответствующим интерфейсом бэкэнда. Это прямое расширение MVC: каждый микро-фронтенд действует как представление для модели своей службы, а родительское приложение (или оболочка) действует как контроллер маршрутизации потоков пользователя между ними.

Подробнее о микро-интерфейсах см. статью Кам Джексона (Cam Jackson) в блоге Мартина Фаулера (Martin Fowler) Micro Frontends.

Преимущества применения принципов MVC к микросервисам

При правильном выполнении, использование MVC-мышления в распределенной среде дает несколько преимуществ, которые выходят за рамки простой организации кода.

Улучшенная модульность

Каждая микрослужба по своей сути имеет четкое разделение между своими внутренними компонентами. Назвав эти компоненты Model, View (если применимо) и Controller, команды могут поддерживать согласованность между службами. Эта модульность облегчает замену реализаций. Например, вы можете заменить механизм устойчивости модели службы, не затрагивая ее API (контроллер) или интерфейс (вид).

Независимая масштабируемость

Поскольку каждая микрослужба является отдельным блоком развертывания, вы можете масштабировать часть «контроллера» (инстанции шлюза API) и часть «модели» (реплицировки службы) независимо. Например, во время флэш-продажи вы можете масштабировать модель Службы заказа горизонтально, чтобы справиться с повышенной загрузкой записи, в то время как модель Службы инвентаризации может нуждаться в другой стратегии масштабирования. Фронтенд (вид) может обслуживаться из CDN и не нуждается в масштабировании с бэкэнд-трафиком.

Автономия команды

Разделение проблем MVC хорошо переводится в командную организацию. Одна команда может владеть «моделью» Платежного сервиса, другая команда может владеть «видом» (микро-интерфейсом пользовательского интерфейса), а команда платформы может владеть шлюзом API (глобальным контроллером). Это согласуется с Законом Конвея: системы напоминают свои коммуникационные структуры. Четко определяя границы MVC между командами, вы снижаете координационные накладные расходы.

Улучшенная проверяемость

Изоляция компонентов облегчает тестирование. Модели обслуживания могут быть протестированы без проблем HTTP. Контроллеры (конечные точки API) могут быть протестированы на интеграцию с макетами. Виды (фронтендные компоненты) могут быть протестированы изолированно с использованием макетных ответов API. Эта многоуровневая стратегия тестирования хорошо известна из монолитного MVC и естественным образом масштабируется в распределенные архитектуры.

Основные проблемы интеграции MVC-Microservices

Хотя преимущества значительны, распределенный характер микросервисов вносит сложности, которых не существует в однопроцессном приложении MVC. Игнорирование этих проблем может привести к хрупким системам, которые труднее поддерживать, чем монолитная альтернатива.

Распределенное управление транзакциями

В монолитном приложении MVC модель часто использует одну базу данных, делая транзакции ACID простыми. В микросервисах каждая служба имеет свою собственную базу данных. Бизнес-операция, которая охватывает несколько служб (например, размещение инвентаризации скидок заказа и взимание платы за кредитную карту), не может использовать одну распределенную транзакцию, не жертвуя доступностью. Вместо этого должны использоваться такие шаблоны, как сага (хореография или оркестровка). Это добавляет сложность к уровню контроллера: сага оркестровки по существу является распределенным контроллером, который координирует несколько моделей обслуживания.

Команды часто недооценивают усилия, необходимые для правильного внедрения саг. Для практического руководства см. Сага шаблон на microservices.io.

Последовательность и задержка данных

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

Более того, контроллер шлюза API должен изящно обрабатывать частичные сбои. Если один сервис ниже по течению выходит из строя, шлюз может вернуть частичный ответ или ухудшенный вид. Это гораздо сложнее, чем монолитный контроллер, который либо преуспевает, либо выходит из строя атомарно.

Сервис Discovery и коммуникации накладные расходы

В монолитном приложении MVC контроллер напрямую вызывает методы модели в том же процессе. В микросервисах эти вызовы становятся сетевыми вызовами. Это увеличивает задержку и вводит потенциальные сбои (тайм-ауты, повторные попытки, выключатели). Слой контроллера должен включать в себя паттерны устойчивости. Кроме того, механизмы обнаружения сервисов (например, Consul, Kubernetes DNS) необходимы для определения местоположения услуг модели во время выполнения. Это добавляет сложность инфраструктуры, к которой многие команды не готовы.

Версии и эволюция

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

Практические шаблоны микросервисов MVC-Aware

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

Backend for Frontend (BFF) (недоступная ссылка)

Эта схема расширяет концепцию контроллера, создавая отдельные API шлюзы для каждого типа клиента (веб, мобильный, IoT). Каждый BFF действует как контроллер, адаптированный к потребностям конкретного представления. Он собирает данные из нескольких моделей обслуживания и отправляет оптимизированный ответ. Это позволяет избежать проблемы общего API шлюза, который заставляет фронтенд-команды справляться со сложными преобразованиями данных.

Модель BFF является естественной для MVC: BFF является контроллером, службы нисходящего потока являются моделями, а пользовательский интерфейс клиента является представлением. Каждая команда BFF владеет своим контроллером и представлением, в то время как услуги модели остаются общими.

Разделение ответственности командных запросов (CQRS)

CQRS разделяет операции чтения и записи. В терминах MVC модель разделяется на модель записи (команд) и модель чтения (запросов). Контроллер решает, является ли запрос командой или запросом и направляет его в соответствующую службу. Виды часто потребляют модели чтения непосредственно через оптимизированные API или прогнозы, основанные на событиях. Этот шаблон особенно полезен в микросервисах, поскольку он позволяет настраивать масштабируемость чтения и записи независимо. Например, модель записи службы заказа может быть нормализована для целостности транзакций, в то время как модель чтения может быть денормализована для быстрых запросов, которые питают просмотр.

Коммуникация, управляемая событиями

Вместо прямых синхронных вызовов службы обмениваются данными через события. Контроллер (API gateway или BFF) может испускать командное событие, а модельные службы потребляют его и испускают события результата. Просмотры могут подписываться на события для обновления пользовательского интерфейса в реальном времени. Это согласуется с оригинальной моделью наблюдателя MVC — вид наблюдает изменения модели через события, но теперь эти события распространяются через брокеров сообщений. Это уменьшает связь и повышает устойчивость, но вводит проблему возможной согласованности.

API Composition vs. Command Message (командное сообщение)

Когда контроллеру нужны данные из нескольких моделей, существуют две стратегии: композиция API (контроллер вызывает каждую услугу напрямую) или командные сообщения (контроллер отправляет запрос на хореографию услуг). Композиция API проще, но увеличивает задержку; командные сообщения более сложны, но отделяют контроллер от потока данных. Выбор между ними сродни выбору между синхронным контроллером и асинхронным в MVC.

Лучшие практики для команд, принимающих MVC+Microservices

Основываясь на реальном опыте, рассмотрим следующие рекомендации:

Вывод: MVC как философия, а не как жесткий шаблон

Модель MVC не устарела в эпоху микросервисов. Напротив, ее основной принцип — разделение проблем — еще более важен, когда компоненты распределены по сетям. Однако применение MVC к микросервисам требует перехода от мышления о ней как о структуре класса к представлению о ней как об архитектурной философии, где модели являются сервисными данными, контроллеры — шлюзами и слоями оркестровки, а представления — клиентскими приложениями или микро-фронтендами.

При продуманной реализации микросервисные архитектуры, вдохновленные MVC, выигрывают от модульности, независимой масштабируемости и автономности команды. Проблемы - распределенные транзакции, возможная согласованность и накладные расходы на связь - реальны, но ими можно управлять с помощью таких шаблонов, как BFF, CQRS и дизайн, управляемый событиями. Ключ заключается в том, чтобы избежать MVC, культивируемого грузом, в распределенную среду без решения сложностей. Вместо этого адаптируйте шаблон, чтобы соответствовать реальности границ сети, асинхронной связи и децентрализованной собственности.

В конечном счете, цель остается той же, что и пятьдесят лет назад: построить системы, которые являются ремонтопригодными, проверяемыми и устойчивыми. MVC, при применении на архитектурном уровне, обеспечивает концептуальную основу для достижения этой цели в микросервисах. Для дальнейшего чтения по комбинированию шаблонов книга Строительство микросервисов Сэма Ньюмана является отличным ресурсом, а сайт microservices.io предлагает каталог шаблонов, которые дополняют мышление MVC.