• Товарный склад. Отправка и возвращение заказов клиентов, управление уровнем запасов и т. д.
• Рекомендации. Ожидающая патентования революционная система выдачи рекомендаций, представляющая собой весьма сложный код, написанный командой, в которой больше кандидатов наук, чем в обычной научной лаборатории.
Сначала нужно создать пакеты, представляющие эти контексты, а затем переместить в них существующий код. Используя современные IDE-среды, переместить код можно автоматически посредством рефакторинга, и сделать это пошагово, занимаясь другими делами. Но, чтобы отловить повреждения, возникающие в связи с перемещением кода, нужно все же проводить тестирование, особенно если используется язык с динамической типизацией, в котором IDE-средам выполнять рефакторинг довольно трудно. Со временем мы начинаем замечать, какой код поддается этому легче, а какой совершенно непригоден для данной процедуры. Этот оставшийся код зачастую будет определять, возможно, пропущенные нами ограниченные контенты!
В ходе этого процесса можно также воспользоваться кодом для анализа зависимостей между пакетами. Код должен представлять организацию, поэтому пакеты, представляющие ограниченные контенты, в организации должны взаимодействовать точно так же, как взаимодействуют между собой настоящие подразделения организации в данной области бизнеса. Например, такой инструмент, как Structure 101, позволяет увидеть графический образ зависимостей между пакетами. Если будет замечено что-то неправильное, например что пакет товарного склада зависит от кода в финансовом пакете, хотя в реальной организации такой зависимости нет, мы сможем понять суть проблемы и попытаться ее решить.
Этот процесс может занять целый день при небольшом объеме исходного кода или несколько недель и даже месяцев, когда придется работать с миллионами строк кода. Вам может не понадобиться сортировка всего кода по ориентированным на отдельные области пакетам перед выделением своего первого сервиса, и даже более того, может оказаться полезнее сконцентрироваться на одном месте. Эта работа не должна представлять собой стремительный процесс. Ее можно сделать пошагово, день за днем, и в нашем распоряжении имеется множество инструментов для отслеживания процесса.
Итак, мы организовали исходный код по стыкам. Что же делать дальше?
Для начала подойдет такое решение: вам хотелось бы, чтобы монолитный сервис или приложение имели меньший объем. Я бы настоятельно рекомендовал урезать эти системы. По ходу дела вы постепенно изучите микросервисы, и это поможет ограничить влияние неверных шагов на всю работу (а таких шагов вам просто не избежать!). Подумайте о нашем монолите как о куске мрамора. Мы могли бы сразу взорвать его, но это редко заканчивается хорошо. Намного разумнее обтесывать кусок постепенно.
Итак, если мы собрались разбивать монолит по кусочку, то с чего начать? Теперь у нас есть стыки, но какой из них нужно вынуть первым? Нужно подумать о том, где вы собираетесь получить наибольшую выгоду от части исходного кода, подлежащей выделению, а не просто разбивать ради самого разбиения. Рассмотрим ряд определяющих аспектов, которые помогут управлять долотом.
Возможно, мы знаем, что находимся на пороге больших изменений в способах управления запасами. Если теперь мы сделаем скол по стыку товарного склада и представим отколовшийся кусок в виде сервиса, то сможем внести изменения в этот сервис быстрее, поскольку теперь он станет автономной единицей.
Команда доставки MusicCorp фактически разделена между двумя географическими регионами. Одна команда находится в Лондоне, а другая на Гавайях (везет же людям!). Было бы здорово выделить код, с которым работает преимущественно гавайская команда, чтобы он перешел в ее полное владение. Эта идея рассматривается в главе 10.
Компания MusicCorp проверила систему безопасности и решила ужесточить меры защиты конфиденциальной информации. На данный момент все управляется кодом, связанным с финансовыми операциями. Если вычленить этот сервис, то можно будет обеспечить для него дополнительные меры защиты в плане мониторинга, защиты передаваемых данных и защиты содержащихся данных. Эти меры защиты подробнее рассматриваются в главе 9.
Команда, присматривавшая за нашей системой рекомендаций, столкнулась с трудностями применения новых алгоритмов с использованием библиотеки логического программирования на языке Clojure. Ее члены посчитали, что это сможет принести пользу клиентам, повысив качество того, что мы им предлагаем. Если бы можно было вычленить код системы выдачи рекомендаций в отдельную службу, то вопрос о ее альтернативной реализации с возможностью тестирования решался бы намного проще.
Другой вопрос, который нужно рассмотреть, когда определены несколько стыков для разделения монолита, касается переплетения этого кода со всей остальной системой. Нам по возможности нужно выявить такой стык, у которого меньше всего зависимостей. Если есть возможность просмотреть различные стыки в виде непосредственного ациклического графа зависимостей (иногда для этого отлично подходят ранее упомянутые мною пакеты средств моделирования), это может помочь в выявлении тех стыков, которые, скорее всего, будет сложнее освободить от зависимостей.
Это подводит нас к тому, что часто служит источником запутанных зависимостей, — к базе данных.
Проблемы использования баз данных в качестве средства интеграции нескольких сервисов подробно обсуждались ранее. Как я абсолютно ясно дал понять, я не сторонник этого способа интеграции! Это означает, что нам нужно найти стыки и в базе данных, чтобы по ним можно было провести четкое разбиение. Но базы данных — весьма хитрые звери.
Для начала нужно посмотреть на сам код и понять, какие его части занимаются чтением из базы данных и записью в нее. Обычно для привязки кода к базе данных и облегчения отображения объектов или структур данных на базу данных и обратно используется уровень хранилища, поддерживаемый какой-либо средой вроде Hibernate. Если до сих пор вы следовали нашим предписаниям, то у вас должен быть код, сгруппированный в пакеты, являющиеся представлениями наших ограниченных контекстов. С кодом доступа к базам данных мы хотим сделать то же самое. Для этого может потребоваться разбиение уровня хранилища на несколько частей (рис. 5.1).
Рис. 5.1. Разбиение уровней хранилищ
Наличие кода отображения на базу данных, расположенного внутри кода для заданного контекста, может помочь разобраться в том, какие части базы данных используются тем или иным фрагментом кода. Например, среда Hibernate может прояснить ситуацию, если вы используете что-либо вроде файла отображения для каждого ограниченного контекста.
Но полной картины мы, конечно же, не получим. Например, мы можем получить возможность определения того, что код финансов использует таблицу главной бухгалтерской книги, а код каталога — таблицу товарных позиций, но при этом может быть не выяснено, что база данных использует внешний ключ, связывающий первую таблицу со второй. Чтобы на уровне базы данных увидеть такие ограничения, на которых можно споткнуться, нужно воспользоваться другим инструментальным средством визуализации данных. Для начала было бы неплохо воспользоваться таким свободно распространяемым средством, как SchemaSpy, которое может сгенерировать графическое представление взаимоотношений между таблицами.
Все это помогает разобраться в связях между таблицами, которые могут перекрывать то, что со временем станет границами сервисов. Но как разорвать эти связи? И что делать в том случае, когда одни и те же таблицы используются из нескольких ограниченных контекстов? Разобраться с подобными проблемами не так-то просто, и на эти вопросы есть масса ответов, но все же это выполнимо.
Возвращаясь к конкретным примерам, еще раз рассмотрим наш музыкальный магазин. Мы определили четыре ограниченных контекста и хотим пойти дальше и сделать на их основе четыре различных, совместно работающих сервиса. Мы собираемся рассмотреть несколько конкретных примеров тех проблем, с которыми могли бы столкнуться, а также потенциальные решения этих проблем. И хотя некоторые из этих примеров относятся именно к тем сложностям, которые встречаются в ходе работы со стандартными реляционными базами данных, сходные проблемы могут возникнуть и во время работы с другими магазинами, в программных средствах которых используется язык SQL.
В этом примере код каталога использует типичная таблица товарных позиций, хранящая информацию об альбоме. А для отслеживания финансовых транзакций код финансов использует таблицу главной бухгалтерской книги. В конце каждого месяца нам нужно составлять отчеты для различных должностных лиц организации, чтобы они могли видеть состояние наших дел. Хочется сделать отчеты красивыми и легкими для чтения, поэтому вместо того, чтобы сообщить: «Мы продали 400 копий SKU 12345 и выручили на этом 1300 долларов», есть желание добавить дополнительную информацию о том, что именно было продано (то есть «Мы продали 400 копий Bruce Springsteen’s Greatest Hits и выручили на этом 1300 долларов»). Чтобы добиться желаемого результата, код составления отчетов в финансовом пакете должен добраться до таблицы товарных позиций и извлечь заголовок для SKU. В нем, как показано на рис. 5.2, могут существовать ограничения, связанные с использованием внешнего ключа от таблицы главной бухгалтерской книги к таблице товарных позиций.
Рис. 5.2. Взаимоотношения, обусловленные наличием внешнего ключа
Итак, как же здесь можно исправить положение? Нужно внести изменения в двух местах. Следует прекратить доступ финансового кода к таблице товарных позиций, поскольку эта таблица принадлежит коду каталога, а мы не хотим, чтобы при вступивших в свои права сервисах каталога и финансов происходила интеграция посредством базы данных. Быстрее всего решить эту проблему, заменив тот код в финансах, который обращался к таблице товарных позиций, выставлением данных через обработку в пакете каталога API-вызова, совершаемого кодом финансов. Как показано на рис. 5.3, этот API-вызов может быть предвестником того вызова, который мы сделаем по сети.