# Путь Shoprocket к микросервисам, часть 1: в чём я ошибся, разбивая монолит

> Пост инженерной команды из первых лет Shoprocket о первой попытке выделить сервис из PHP-монолита. Почему я выбрал не тот первый сервис, чему меня научила сильная связанность и какой совет Ньюмена надо было послушать раньше.

Winston Berty  - 17 марта 2016 г. - 5 мин чтения

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

У нас было монолитное приложение, почти целиком в одном репозитории GitHub. Обновления или исправления одной функции часто требовали правок в нескольких местах. Для тестирования у нас было ревью коллег: как минимум двое других разработчиков должны были одобрить ветку, прежде чем она сливалась в master и выкатывалась. Всё это означало, что когда баг просачивался, найти сломавшее изменение было часто трудно. А после того как мы его находили, исправление проходило тот же процесс, и приложение приходилось выкатывать целиком заново.

Я был увлечённым молодым разработчиком, который всегда ищет что-то новое, и когда наткнулся на микросервисы, решил, что они решат все наши проблемы.

## Что я думал о микросервисах

Формального определения микросервисов нет, но из своих изысканий я вынес, что микросервисная архитектура это способ проектировать программные приложения как распределённые системы независимых сервисов, каждый из которых узкоспециализирован и имеет собственный жизненный цикл. Подробное введение есть в отличной [статье](https://martinfowler.com/articles/microservices.html) Мартина Фаулера.

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

Я был убеждён. Shoprocket собирался разбить монолит. Это часть 1 небольшой серии об этом пути. Надеюсь, читатели, которые планируют похожий переход или проходят его, научатся на наших успехах и ошибках.

## Первая трудность была человеческой, а не технической

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

Оглядываясь назад, это был плохой выбор первого сервиса. В нашем продукте есть JavaScript-виджет, который [включает e-commerce на любом сайте](https://v3.shoprocket.io/ru/features/sell-on-any-website.md). Мы рассматриваем сессии как ресурс и сохраняем информацию о них в базе данных для анализа брошенных корзин и других показателей. Но этот ресурс не такой осязаемый, как заказы или позиции заказа, а сессии мы использовали почти при каждом изменении состояния приложения. Это означало, что сервис неизбежно будет «болтливым» по отношению к остальному приложению; я этого не предвидел, и это усложнило мою задачу.

## Чему я научился, когда стал это строить

Я углубил исследование. Наряду с Фаулером незаменимым ресурсом оказалась книга Сэма Ньюмена **Building Microservices**.

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

Я этому совету не последовал. Мне не терпелось построить собственный компонент, и я взялся за сервис сессий.

Я выбрал стек, лишь слегка отличающийся от стека монолита, и начал строить. Сам сервис собрался быстро: за несколько недель получилось отдельное приложение с REST API. Сложность была не в этом. Сложно было заставить сервис разговаривать с монолитом.

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

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

## Честная оценка

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

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

Тем, кто задумывается о похожем переходе, мои честные рекомендации:

- Прочитайте как можно больше по теме, прежде чем решаться
- Отложите работу, пока вы действительно не уверены в своей кодовой базе и модели предметной области
- Помните, что есть и другие способы добавить монолиту стабильности и устойчивости. Автоматические модульные и функциональные тесты помогают колоссально. Разработка через тестирование снижает число регрессий. Горизонтальное масштабирование делает деплои менее болезненными
- Начните с модуляризации монолита, а уже потом выделяйте сервисы

Shoprocket продолжит свой путь. В части 2 станет интереснее.

---

*Первоначально опубликовано как первая статья небольшой серии о ранней миграции Shoprocket с PHP-монолита. Весь путь, включая ошибки, исправленные в последующих итерациях, продолжился во всех крупных переписываниях платформы с тех пор.*

---

- HTML version: https://v3.shoprocket.io/ru/blog/shoprockets-journey-to-microservices-part-1-what-i-got-wrong-about-breaking-up-the-monolith
- Language: Русский (ru)
- Also in: [العربية](https://v3.shoprocket.io/ar/blog/shoprockets-journey-to-microservices-part-1-what-i-got-wrong-about-breaking-up-the-monolith.md), [Deutsch](https://v3.shoprocket.io/de/blog/shoprocket-microservices-teil-1-monolith-zerlegen-fehler.md), [Ελληνικά](https://v3.shoprocket.io/el/blog/shoprockets-journey-to-microservices-part-1-what-i-got-wrong-about-breaking-up-the-monolith.md), [English](https://v3.shoprocket.io/blog/shoprockets-journey-to-microservices-part-1-what-i-got-wrong-about-breaking-up-the-monolith.md), [Español](https://v3.shoprocket.io/es/blog/shoprocket-microservicios-parte-1-lo-que-hice-mal-monolito.md), [Français](https://v3.shoprocket.io/fr/blog/shoprocket-vers-les-microservices-partie-1-decouper-le-monolithe.md), [עברית](https://v3.shoprocket.io/he/blog/shoprockets-journey-to-microservices-part-1-what-i-got-wrong-about-breaking-up-the-monolith.md), [हिन्दी](https://v3.shoprocket.io/hi/blog/shoprockets-journey-to-microservices-part-1-what-i-got-wrong-about-breaking-up-the-monolith.md), [Bahasa Indonesia](https://v3.shoprocket.io/id/blog/perjalanan-shoprocket-menuju-microservices-bagian-1-salah-memecah-monolit.md), [Italiano](https://v3.shoprocket.io/it/blog/viaggio-di-shoprocket-verso-i-microservizi-parte-1-cosa-ho-sbagliato-sul-monolite.md), [日本語](https://v3.shoprocket.io/ja/blog/shoprockets-journey-to-microservices-part-1-what-i-got-wrong-about-breaking-up-the-monolith.md), [Lietuvių](https://v3.shoprocket.io/lt/tinklarastis/shoprocket-kelione-i-mikropaslaugas-1-dalis.md), [Norsk](https://v3.shoprocket.io/nb/blogg/shoprockets-vei-mot-mikrotjenester-del-1-hva-jeg-tok-feil-av.md), [Nederlands](https://v3.shoprocket.io/nl/blog/shoprocket-microservices-deel-1-monoliet-opknippen.md), [Polski](https://v3.shoprocket.io/pl/blog/droga-shoprocket-do-mikroserwisow-czesc-1-rozbijanie-monolitu.md), [Português](https://v3.shoprocket.io/pt/blog/jornada-do-shoprocket-para-microsservicos-parte-1-o-que-errei-ao-quebrar-o-monolito.md), [Română](https://v3.shoprocket.io/ro/blog/drumul-shoprocket-spre-microservicii-partea-1-ce-am-gresit-cu-monolitul.md), [ไทย](https://v3.shoprocket.io/th/blog/shoprockets-journey-to-microservices-part-1-what-i-got-wrong-about-breaking-up-the-monolith.md), [Türkçe](https://v3.shoprocket.io/tr/blog/shoprocket-mikroservis-yolculugu-1-monoliti-parcalarken-yanlis-yaptiklarim.md), [中文](https://v3.shoprocket.io/zh/blog/shoprockets-journey-to-microservices-part-1-what-i-got-wrong-about-breaking-up-the-monolith.md)
