автордың кітабын онлайн тегін оқу Современный подход к программной архитектуре: сложные компромиссы
Переводчик Л. Киселева
Нил Форд, Марк Ричардс , Прамод Садаладж , Жамак Дехгани
Современный подход к программной архитектуре: сложные компромиссы. — СПб.: Питер, 2023.
ISBN 978-5-4461-2024-6
© ООО Издательство "Питер", 2023
Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.
Отзывы о книге «Современный подход к программной архитектуре: сложные компромиссы»
Эта книга — долгожданное руководство по созданию микросервисов и анализу нюансов архитектурных решений по всему стеку технологий. Она может служить каталогом архитектурных решений, которые могут приниматься при построении распределенной системы, и описывает плюсы и минусы каждого решения. Ее должен прочитать каждый архитектор, занимающийся созданием современных распределенных систем.
Александар Серафимоски (Aleksandar Serafimoski), ведущий консультант, Thoughtworks
Бесценная книга для технологов, увлеченных разработкой архитектур. Содержит превосходное описание паттернов.
Ванья Сетх (Vanya Seth), руководитель технического отдела, Thoughtworks, Индия
Кем бы вы ни были — начинающим архитектором или опытным руководителем команды, эта книга, обойдясь без словоблудия, расскажет вам, как добиться успеха на пути к созданию корпоративных приложений и микросервисов.
Д-р Венкат Субраманиам (Venkat Subramaniam), обладатель множества наград, автор и основатель Agile Developer, Inc.
Эта книга содержит ценную информацию, практические советы и примеры из реальной жизни, посвященные разделению тесно связанных систем и их повторному созданию. Приобретя навыки эффективного анализа компромиссов, вы начнете принимать более эффективные архитектурные решения.
Йост ван Винен (Joost van Weenen), управляющий партнер и сооснователь Infuze Consulting
Мне очень понравился этот всеобъемлющий труд по распределенным архитектурам! Отличное сочетание серьезного обсуждения, фундаментальных идей и множества практических советов.
Дэвид Клоэт (David Kloet), независимый архитектор программного обеспечения
Разбить большой ком грязи — непростая работа. Начиная с кода и заканчивая данными, эта книга поможет увидеть, какие сервисы следует отделить и сделать независимыми, а какие оставить работать вместе.
Рубен Диас-Мартинес (Rubén Díaz-Martínez), разработчик программного обеспечения в Codesai
Эта книга поможет вам заложить теоретическую и практическую основы и ответить на самые сложные вопросы, возникающие при разработке современных архитектур программного обеспечения.
Джеймс Льюис (James Lewis), технический директор, Thoughtworks
Вступление
Работая над книгой Fundamentals of Software Architecture1, мы (Нил и Марк) постоянно сталкивались со сложными примерами архитектур, о которых нам очень хотелось рассказать, но которые были слишком трудными. Ни один из них не имел простого решения, и каждый являл собой клубок запутанных компромиссов. Мы сложили эти примеры в стопку, которую назвали «Сложные компромиссы». Как только та книга была закончена, мы посмотрели на эту огромную стопку и попытались выяснить: почему эти проблемы так трудно решить в современных архитектурах?
Мы проработали все примеры как архитекторы, применяя анализ компромиссов для каждой ситуации, а также обращая внимание на процесс, с помощью которого достигались компромиссы. Одним из наших первых открытий стало возрастающее значение данных в архитектурных решениях: кто может/должен иметь доступ к данным, кто может/должен формировать и изменять их и как управлять разделением аналитических и операционных данных. Чтобы это понять, мы пригласили экспертов в указанных областях, что позволило увидеть весь процесс принятия решений с обеих сторон: от архитектуры к данным и от данных к архитектуре.
Результатом стала эта книга: сборник сложных проблем в современной архитектуре программного обеспечения, компромиссов, усложняющих принятие решений, и, наконец, иллюстрированное руководство, показывающее, как применять тот же анализ компромиссов к вашим собственным уникальным задачам.
Условные обозначения
В этой книге приняты следующие обозначения.
Курсив
Курсивом выделены новые термины и ключевые понятия.
Моноширинный шрифт
Используется для листингов программ, а также внутри абзацев для обозначения таких элементов, как переменные и функции, базы данных, типы данных, переменные среды, операторы и ключевые слова, имена файлов и их расширений.
Моноширинный жирный шрифт
Показывает команды или другой текст, который пользователь должен ввести самостоятельно.
Моноширинный курсив
Показывает текст, который должен быть заменен значениями, введенными пользователем, или значениями, определяемыми контекстом.
Шрифт без засечек
Используется для обозначения URL, адресов электронной почты, названий кнопок, каталогов.
Этот рисунок указывает на совет или предложение.
Использование программного кода примеров
Вспомогательные материалы (примеры кода, упражнения и т.д.) доступны для скачивания по адресу http://architecturethehardparts.com/.
Если у вас есть вопросы технического характера или возникли проблемы с использованием примеров кода, то присылайте их по адресу bookquestions@oreilly.com.
В общем случае все примеры кода из книги вы можете использовать в своих программах и в документации. Вам не нужно обращаться в издательство за разрешением, если вы не собираетесь воспроизводить существенные части программного кода. Если вы разрабатываете программу и используете в ней несколько фрагментов кода из книги, вам не нужно обращаться за разрешением. Но для продажи или распространения примеров из книги вам потребуется разрешение от издательства O’Reilly. Вы можете отвечать на вопросы, цитируя данную книгу или примеры из нее, но для включения существенных объемов программного кода из книги в документацию вашего продукта потребуется разрешение.
Мы рекомендуем, но не требуем добавлять ссылку на первоисточник при цитировании. Под ссылкой на первоисточник мы подразумеваем указание авторов, издательства и ISBN.
За получением разрешения на использование значительных объемов программного кода из книги обращайтесь по адресу permissions@oreilly.com.
Благодарности
Мы, Марк и Нил, выражаем благодарность всем, кто посещал наши (почти исключительно онлайн) занятия, семинары, конференции и встречи групп пользователей, а также всем, кто выслушивал наши доклады по данной теме и оставлял бесценные отзывы. Многократное повторное обсуждение нового материала — особенно сложная задача, когда нельзя сделать это вживую, поэтому мы очень ценим усилия всех, кто оставался с нами на протяжении множества итераций. Мы благодарим команду издательства O’Reilly, сделавшую процесс написания книги настолько безболезненным, насколько это возможно. Мы также благодарим отдельные группы, такие как Pasty Geeks и Hacker B&B, являющиеся оазисами здравомыслия и источниками искрометных идей.
Спасибо всем рецензентам нашей книги: Ванье Сетху (Vanya Seth), Венкату Субраманиану (Venkat Subramanian), Йосту ван Винену (Joost van Weenen), Грэди Бучу (Grady Booch), Рубену Диасу (Ruben Diaz), Дэвиду Клоэту (David Kloet), Мэтту Штейну (Matt Stein), Данило Сато (Danilo Sato), Джеймсу Льюису (James Lewis) и Сэму Ньюману (Sam Newman). Ваши знания и отзывы помогли улучшить эту книгу.
Мы хотим выразить особую признательность многим работникам и семьям, пострадавшим от неожиданной глобальной пандемии. Как работники умственного труда, мы столкнулись с неудобствами, которые не выдерживают никакого сравнения с массовыми потрясениями и разрушениями во всех сферах жизни, причиненными многим нашим друзьям и коллегам. Мы особенно сочувствуем и признательны работникам здравоохранения, многие из которых не думали, что когда-нибудь окажутся на переднем крае ужасной глобальной трагедии, но достойно справились с ней. Наша благодарность вам бесконечна.
От Марка Ричардса
В дополнение к написанному выше я хочу еще раз поблагодарить мою прекрасную супругу Ребекку (Rebecca) за то, что она терпела, пока я занимался еще одним книжным проектом. Твоя неиссякаемая поддержка и советы помогли мне написать эту книгу, и это притом, что тебе приходилось отвлекаться от работы над собственным романом. Ребекка, ты для меня — целый мир. Я также благодарю моего доброго друга и соавтора Нила Форда. Совместная работа с тобой над этой книгой (и над предыдущей) была по-настоящему ценным и полезным опытом. Ты есть и всегда будешь моим другом.
От Нила Форда
Я хотел бы поблагодарить свою большую семью: весь коллектив Thoughtworks, а также Ребекку Парсонс (Rebecca Parsons) и Мартина Фаулера (Martin Fowler). Thoughtworks — выдающаяся группа людей, которые создают для клиентов ценные решения, внимательно следят за их работой и выясняют, что еще в них можно улучшить. Компания Thoughtworks во многом поддержала эту книгу и продолжает растить мыслителей, бросающих мне вызов и вдохновляющих меня каждый день. Я также благодарю наш коктейль-клуб, находящийся по соседству, за регулярную возможность сбежать от рутины и в том числе за еженедельные встречи на свежем воздухе, этакие социально дистанцированные версии, которые помогли всем нам пережить пандемию. Я благодарю своего давнего друга Нормана Запиена (Norman Zapien), который постоянно дарит мне приятные беседы. Наконец, спасибо моей жене Кэнди (Candy), благодаря которой у меня есть возможность тратить много времени на такие занятия, как написание книг, а не на уход за нашими кошками.
От Прамода Садаладжа
Я благодарю свою супругу Рупали (Rupali) за поддержку и понимание и моих прекрасных девочек Арулу (Arula) и Архану (Arhana) за воодушевление; папа любит вас обеих. Все, что я делаю, было бы невозможно без клиентов, с которыми я работаю, и различных конференций, помогающих мне проверять и пересматривать идеи и понятия. Я благодарю AvidXchange, последнего клиента, с которым я работаю, за его поддержку и предоставленную возможность проверки новых концепций. Я также благодарю Thoughtworks за постоянную поддержку, а также Нила Форда (Neal Ford), Ребекку Парсонс (Rebecca Parsons) и Мартина Фаулера (Martin Fowler), ставших мне замечательными наставниками; вы помогаете мне стать лучше. Наконец, спасибо моим родителям, особенно моей маме Шобхе (Shobha), по которой я постоянно скучаю. Я очень скучаю по тебе, мама.
От Жамак Дехгани
Я благодарю Марка и Нила за их приглашение внести свой вклад в эту удивительную книгу. Но это было бы невозможно без постоянной поддержки моего мужа Адриана (Adrian) и терпения моей дочери Арианны (Arianna). Я люблю вас обоих.
От издательства
Ваши замечания, предложения, вопросы отправляйте по адресу comp@piter.com (издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
На веб-сайте издательства www.piter.com вы найдете подробную информацию о наших книгах.
1 Форд Н., Ричардс М. Фундаментальный подход к программной архитектуре: паттерны, свойства, проверенные методы. — СПб.: Питер, 2023.
Форд Н., Ричардс М. Фундаментальный подход к программной архитектуре: паттерны, свойства, проверенные методы. — СПб.: Питер, 2023.
Работая над книгой Fundamentals of Software Architecture1, мы (Нил и Марк) постоянно сталкивались со сложными примерами архитектур, о которых нам очень хотелось рассказать, но которые были слишком трудными. Ни один из них не имел простого решения, и каждый являл собой клубок запутанных компромиссов. Мы сложили эти примеры в стопку, которую назвали «Сложные компромиссы». Как только та книга была закончена, мы посмотрели на эту огромную стопку и попытались выяснить: почему эти проблемы так трудно решить в современных архитектурах?
Глава 1. Что происходит, когда нет «передового опыта»
Почему технические специалисты, такие как архитекторы программного обеспечения, выступают на конференциях или пишут книги? Потому что они открыли для себя то, что в просторечии называется «передовым опытом» (best practices). Однако этим термином настолько злоупотребляют, что те, кто его использует, все чаще реагируют негативно. Но, как бы там ни было, технические специалисты пишут книги, когда придумывают новое решение общей проблемы и хотят донести его до широкой аудитории.
Но что происходит с огромным множеством задач, не имеющих хороших решений? В архитектуре программного обеспечения существуют целые классы задач, которые не имеют универсальных и надежных решений и представляют собой запутанный клубок компромиссов.
Разработчики программного обеспечения приобретают недюжинные навыки поиска в Интернете решений текущей задачи. Например, если нужно выяснить, как настроить конкретный инструмент, то разработчики обращаются за ответом к Google.
Но это не относится к архитекторам.
Для архитекторов многие проблемы являются уникальными, поскольку объединяют среду и обстоятельства в конкретной организации — насколько велики шансы, что кто-то уже сталкивался именно с таким сценарием и опубликовал свое решение в блоге или на Stack Overflow?
Архитекторы могут задаваться вопросом, почему так мало книг по архитектуре по сравнению с другими техническими темами, такими как фреймворки, API и т.д. Архитекторы редко сталкиваются с обычными проблемами, им постоянно приходится принимать решения в новых ситуациях. Для архитектора любая задача — снежинка. Во многих случаях задача является новой не только для конкретной организации, но и для всего мира. По этим задачам не существует ни книг, ни конференций!
Архитекторы не должны постоянно искать серебряные пули — универсальные решения своих задач; эти решения так же редки, как и в 1986 году, когда Фред Брукс (Fred Brooks) ввел этот термин.
Нет ни одного открытия ни в технологии, ни в методах управления, одно только использование которого обещало бы в течение ближайшего десятилетия на порядок [в десять раз] повысить производительность, надежность, простоту разработки программного обеспечения.
Фред Брукс, статья No Silver Bullet
Практически каждая задача привносит новые сложности. Поэтому настоящая работа архитектора заключается в его способности объективно определить возможные компромиссы, оценить их и выбрать хорошее решение. Мы не говорим «лучшее решение» (ни в этой книге, ни в реальной жизни), поскольку «лучшее» подразумевает, что архитектору удалось максимально использовать все конкурирующие факторы в проекте. Вместо этого мы шутливо советуем:
Не пытайтесь найти лучшее решение в архитектуре программного обеспечения; стремитесь к наименее худшему сочетанию компромиссов.
Часто лучшее решение, которое может создать архитектор, представляет собой наименее худший набор компромиссов: никакая архитектурная характеристика не является преобладающей, но баланс всех конкурирующих характеристик способствует успеху проекта.
Напрашивается вопрос: «Как архитектор должен искать наименее худшую комбинацию компромиссов (и эффективно документировать их)?» Эта книга в первую очередь посвящена принятию решений и учит архитекторов делать наиболее эффективный выбор в новых ситуациях.
Почему «сложные компромиссы»?
Почему мы назвали эту книгу «Современный подход к программной архитектуре: сложные компромиссы»? На самом деле слово «сложные» в названии выполняет двойную функцию. Во-первых, «сложный» означает «трудный», и архитекторы постоянно сталкиваются с трудными задачами, с которыми буквально (и фигурально) никто не сталкивался раньше, включая многочисленные технологические решения, имеющие долгосрочные последствия, наложенные на межличностное и политическое окружение, в котором должно приниматься решение.
Во-вторых, «сложный» означает «неподатливый», то есть нечто с трудом поддающееся изменениям и образующее основу для чего-то другого, более податливого, как, например, аппаратное обеспечение для программного. Точно так же архитекторы обсуждают различия между выстраиванием архитектуры и проектированием, где первая является структурным понятием, а второе легче изменить. Таким образом, в этой книге мы будем говорить об основополагающих частях архитектуры.
Само определение архитектуры программного обеспечения вызвало многочасовые и бесплодные дискуссии среди практиков. Одно из наших любимых ироничных определений гласит: «Архитектура программного обеспечения — это такая штука, которую потом сложно изменить». Этой самой штуке и посвящена наша книга.
Советы по архитектуре программного обеспечения, неподвластные времени
Экосистема разработки ПО непрерывно и хаотично растет и меняется. Темы, бывшие насущными несколько лет назад, были либо поглощены экосистемой и исчезли, либо заменены чем-то другим/лучшим. Например, десять лет назад преобладающим архитектурным стилем для крупных предприятий была сервисно-ориентированная архитектура. Теперь его практически никто не использует (по причинам, которые мы раскроем по ходу дела); сегодня предпочтительным стилем организации многих распределенных систем являются микросервисы. Как и почему произошел этот переход?
Рассматривая определенный стиль (особенно бывший актуальным в прошлом), архитекторы должны учитывать ограничения, которые обеспечивали доминирующее положение этого стиля. В то время многие компании объединялись, превращаясь в корпорации, и это порождало сопутствующие интеграционные проблемы. Кроме того, крупные компании не считали жизнеспособными решения с открытым исходным кодом (часто по политическим, а не по техническим причинам). Вследствие этого архитекторы сделали упор на общие ресурсы и централизованное управление.
Однако за прошедшие годы открытый исходный код и Linux превратились во вполне жизнеспособные альтернативы, что сделало операционные системы коммерчески бесплатными. Переломный момент наступил, когда Linux стал операционно свободным благодаря появлению таких инструментов, как Puppet и Chef, которые позволили командам разработчиков программно развертывать свои среды в рамках автоматизированной сборки. Эта новая возможность способствовала архитектурной революции микросервисов и быстро развивающейся инфраструктуры контейнеров и инструментов их оркестрации, таких как Kubernetes.
Таким образом, можно увидеть, что экосистема разработки программного обеспечения расширяется и развивается в совершенно неожиданных направлениях. Одна новая возможность ведет к другой, а та неожиданно порождает новые. Со временем экосистема полностью заменяет себя, иногда по частям.
Как следствие, у авторов книг о технологиях в целом и об архитектуре ПО в частности возникает извечная проблема: как написать что-то, что устареет не сразу?
В книге мы не будем фокусироваться на технологиях и других деталях реализации, а сосредоточимся на том, как архитекторы принимают решения и как объективно оценивать компромиссы в новых ситуациях. Мы будем использовать современные сценарии и примеры, чтобы предоставить детали и контекст, но основное наше внимание будет сосредоточено на анализе компромиссов и принятии решений при встрече с новыми проблемами.
Важность данных в архитектуре
Данные — большая ценность, и они будут храниться дольше, чем сами системы.
Тим Бернерс-Ли (Tim Berners-Lee)
Для многих архитектур данные — самое главное. Каждое предприятие, создающее какую-либо систему, вынуждено иметь дело с данными, поскольку они, как правило, живут намного дольше, чем системы или архитектура, и требуют тщательного обдумывания и проектирования. Однако склонность архитекторов данных создавать тесно связанные системы приводит к конфликтам в современных распределенных архитектурах. Например, архитекторы и администраторы баз данных (БД) должны обеспечить выживание бизнес-данных после разделения монолитных систем, чтобы бизнес мог по-прежнему извлекать пользу из своих данных независимо от изменений в архитектуре.
Говорят, что данные — самый важный актив компании. Предприятия хотят извлекать выгоду из имеющихся у них данных и постоянно ищут новые способы их использования при принятии решений. Каждое подразделение предприятия теперь управляется данными, от обслуживания существующих клиентов до привлечения новых, удержания клиентов, улучшения продуктов, прогнозирования продаж и других целей. Такая зависимость от данных означает, что вся программная архитектура служит данным, обеспечивая всем подразделениям предприятия доступ к нужным данным и возможность их использования.
За несколько десятилетий авторы книги создали множество распределенных систем, когда они только начали входить в моду. Однако принимать решения в современных микросервисных архитектурах оказалось намного сложнее, и мы хотели выяснить почему. В конечном итоге мы пришли к выводу, что проблема в сохранении всех данных в одной реляционной базе данных — привычке, сложившейся еще перед появлением распределенных архитектур. Однако в микросервисах и предметно-ориентированном проектировании (https://oreil.ly/bW8CH), согласно философии ограничения контекста как способа ограничения видимости деталей реализации, данные, вкупе с управлением транзакциями, переместились на архитектурный уровень. Многие из сложных компромиссов современной архитектуры, которым мы уделим внимание в частях I и II, возникают из-за противоречий между данными и архитектурой.
Одно важное различие, которое мы рассматриваем в разных главах, — разделение операционных и аналитических данных.
• Операционные данные используются для ведения бизнеса, включая данные о продажах, сделках, запасах и т.д. На этих данных основана работа компании — если что-то прерывает их поток, то организация долгое время не может нормально функционировать. Этот тип данных определяется как обработка транзакций в реальном времени (Online Transactional Processing, OLTP), которая обычно включает добавление, изменение и удаление данных в базе данных.
• Аналитические данные используются аналитиками для прогнозирования, определения тенденций и других бизнес-исследований. Эти данные, как правило, не являются транзакционными и часто имеют нереляционный характер. Они могут храниться в графовой базе данных или в моментальных снимках в форме, отличной от исходной транзакционной формы. Эти данные не имеют решающего значения для повседневной работы и обычно используются для долгосрочного планирования и принятия стратегических решений.
В книге мы будем рассматривать влияние обоих видов данных: и операционных, и аналитических.
Запись архитектурных решений
Один из наиболее эффективных способов документирования архитектурных решений — запись в реестре архитектурных решений (Architectural Decision Records, ADR (https://adr.github.io/)). Впервые использовать ADR предложил Майкл Найгард (Michael Nygard) в своей статье в блоге (https://oreil.ly/yDcU2), а затем они были отмечены как «принятые» в сборнике трендов/технологий Thoughtworks Technology Radar (https://oreil.ly/0nwHw). ADR состоит из короткого текстового файла (обычно одна-две страницы), описывающего конкретное архитектурное решение. ADR могут быть оформлены как обычные текстовые файлы, но чаще для их оформления применяется какой-либо текстовый формат, такой как AsciiDoc (http://asciidoc.org/) или Markdown (https://www.markdownguide.org/). Кроме того, запись ADR можно оформить с помощью шаблона вики-страницы. Мы посвятили ADR целую главу в нашей предыдущей книге Fundamentals of Software Architecture2 (https://learning.oreilly.com/library/view/fundamentals-of-software/9781492043447).
Мы будем использовать ADR как способ документирования различных архитектурных решений, принимаемых на протяжении всей книги. Для описания каждого архитектурного решения будем использовать следующий формат ADR, предполагая, что каждая запись ADR одобрена.
ADR: короткое именное словосочетание, содержащее архитектурное решение.
Контекст
В этом разделе ADR мы будем приводить краткое описание задачи, состоящее из одного-двух предложений, и перечислять альтернативные решения.
Решение
В этом разделе мы изложим архитектурное решение и его подробное обоснование.
Последствия
В этом разделе ADR мы опишем последствия применения решения, а также обсудим рассмотренные компромиссы.
Список всех записей ADR, созданных в этой книге, можно найти в приложении Б.
Документирование решений важно для архитектора, но управление правильным использованием решения — отдельная тема. К счастью, современные инженерные методы позволяют автоматизировать многие распространенные задачи управления с помощью функций пригодности для архитектуры.
Функции пригодности
Определив взаимосвязи между компонентами и отразив их в проекте, архитектор должен убедиться, что разработчики будут придерживаться этого проекта. Но как это сделать? Как вообще архитекторы могут гарантировать реализацию определяемых ими архитектурных принципов, если не являются теми, кто этим занимается?
Эти вопросы относятся к категории управления архитектурой, применимой к любому организованному надзору за аспектами разработки программного обеспечения. Поскольку эта книга в основном посвящена конструированию архитектуры, мы расскажем, как автоматизировать принципы проектирования и качества с помощью функций пригодности (fitness functions).
Разработка ПО медленно развивалась во времени, адаптируя уникальные инженерные методы. На заре разработки и к крупным процессам (таким как водопадный процесс разработки), и к малым (практики интеграции внутри проектов) обычно применялась метафора производства. В начале 1990-х работы по переоценке инженерных методов разработки ПО, проведенные инженерами проекта C3 под руководством Кента Бека (Kent Beck), привели к созданию методологии экстремального программирования (eXtreme Programming, XP) и показали важность дополнительной обратной связи и автоматизации как ключевых факторов повышения производительности разработчиков. В начале 2000-х те же уроки были применены на стыке разработки и эксплуатации программного обеспечения, благодаря чему родилась новая роль DevOps и были автоматизированы многие рутинные операции, ранее выполняемые вручную. Как и раньше, автоматизация позволяет командам работать быстрее, поскольку им не нужно беспокоиться о том, что что-то пойдет не так и они не получат своевременную обратную связь. Как следствие, автоматизация и обратная связь стали центральными принципами эффективной разработки ПО.
Рассмотрим среды и ситуации, мешающие автоматизации. До появления непрерывной интеграции большинство программных проектов предусматривало продолжительный этап интеграции. Предполагалось, что каждый разработчик будет работать до определенной степени изолированно от других, а затем все вместе они будут объединять код на этапе интеграции. Следы этой практики все еще сохраняются в инструментах управления версиями, которые вызывают ветвление и препятствуют непрерывной интеграции. Неудивительно, что существовала сильная корреляция между размером проекта и сложностью этапа интеграции. Внедрив непрерывную интеграцию, команда, практикующая экстремальное программирование (XP), продемонстрировала ценность быстрой и непрерывной обратной связи.
Революция DevOps пошла по тому же пути. Linux и другое ПО с открытым исходным кодом становилось «достаточно хорошим» для предприятий. Вдобавок появлялись инструменты, позволявшие программно определять (в конечном итоге) виртуальные машины. По мере всего этого операционный персонал понял, что может автоматизировать определение машин и многие другие повторяющиеся задачи.
В обоих случаях достижения в области технологий и знаний привели к автоматизации повторяющихся задач, решение которых прежде обходилось довольно дорого, что описывает текущее состояние управления архитектурой в большинстве организаций. Например, выбрав определенный архитектурный стиль или средство коммуникации, как архитектор сможет убедиться, что разработчик правильно его реализует? Когда все делается вручную, архитекторы проводят обзоры кода или собирают комиссию для анализа архитектуры, чтобы получить оценку состояния управления. Однако, как и при ручной настройке компьютеров в процессе эксплуатации, при поверхностном рассмотрении важные детали легко могут остаться незамеченными.
Использование функций пригодности. В книге Building Evolutionary Architectures3 (O’Reilly) 2017 года авторы (Нил Форд, Ребекка Парсонс и Патрик Куа) определили концепцию функции пригодности для архитектуры: любой механизм, объективно оценивающий целостность некой архитектурной характеристики или их комбинации. Коротко разберем это определение по пунктам.
• Любой механизм. Архитекторы могут реализовывать функции пригодности с помощью широкого спектра инструментов; в этой книге мы покажем множество примеров. Например, существуют специальные библиотеки тестирования, позволяющие проверять структуру архитектуры; с помощью мониторов архитекторы могут тестировать операционные характеристики архитектуры, такие как производительность или масштабируемость, а фреймворки хаос-инжиниринга служат для проверки надежности и отказоустойчивости.
• Объективная оценка целостности. Один из ключевых факторов автоматизированного управления — объективное определение характеристик архитектуры. Например, архитектор не может просто сказать, что ему нужен «высокопроизводительный» сайт; он должен представить величину, которую можно измерить с помощью теста, монитора или другой функции пригодности.
Архитекторы должны следить за составными архитектурными характеристиками, не поддающимися объективному измерению и являющимися составными частями других измеримых показателей. Например, «гибкость» не поддается измерению. Но если разобрать широкий термин «гибкость» с точки зрения архитектора, то оказывается, что цель состоит в быстром и уверенном реагировании команды на изменения в экосистеме или предметной области. Соответственно, архитектор может найти измеримые характеристики, отражающие степень гибкости: возможность развертывания, тестируемость, продолжительность цикла разработки и т.д. Часто отсутствие возможности измерить архитектурную характеристику указывает на слишком расплывчатое определение. Стремление найти измеримые свойства позволяет архитекторам автоматизировать применение функций пригодности.
• Некая архитектурная характеристика или их комбинация. Архитектурная характеристика описывает два вида функций пригодности:
• атомарные оценивают единственную архитектурную характеристику. Например, функция пригодности, проверяющая наличие циклических зависимостей компонентов в кодовой базе, является атомарной;
• комплексные проверяют комбинацию архитектурных характеристик. Усложняющей особенностью архитектурных характеристик является синергия с другими характеристиками, которую они иногда демонстрируют. Например, если архитектор хочет улучшить безопасность, то велика вероятность, что это повлияет на производительность. Точно так же масштабируемость и адаптируемость иногда противоречат друг другу — поддержка большого количества одновременно работающих пользователей может затруднить обработку внезапных всплесков. Комплексные функции пригодности проверяют комбинацию взаимосвязанных архитектурных характеристик и оценивают негативное влияние комбинированного эффекта на архитектуру.
Архитектор реализует функции пригодности в целях защиты от неожиданных изменений архитектурных характеристик. В мире гибкой разработки программного обеспечения разработчики реализуют модульные и функциональные тесты, а пользователи — приемочные, чтобы проверить различные предметные аспекты. Однако до сих пор не существовало подобного механизма, проверяющего архитектурные свойства проекта. На самом деле разделение между функциями пригодности и модульными тестами — хороший ориентир для архитекторов. Функции пригодности проверяют архитектурные свойства, а не предметные; модульные тесты делают обратное. Соответственно, архитектор может определить, что именно нужно, функция пригодности или модульный тест, задав вопрос: «Требуются ли какие-либо знания предметной области для этой проверки?» Если ответ «да», то уместно модульное/функциональное/приемочное тестирование; если «нет», то нужна функция пригодности.
Например, говоря об адаптируемости, архитекторы подразумевают способность приложения выдерживать внезапный наплыв пользователей. Обратите внимание, что в этом случае архитектору не нужно знать никаких подробностей о предметной области — это может быть сайт электронной коммерции, онлайн-игра или что-то еще. Поэтому адаптируемость является архитектурной характеристикой, и для ее оценки требуется функция пригодности. Напротив, проверять правильность элементов адреса электронной почты нужно с помощью традиционного теста. Конечно, такое разделение не является четким и ясным — некоторые функции пригодности будут касаться предметной области и наоборот, но осознание разных целей помогает мысленно разделить их.
Приведем несколько примеров, чтобы сделать обсуждение менее абстрактным.
Одна из типичных целей архитектора — обеспечить хорошую внутреннюю структурную целостность кодовой базы. Однако на многих платформах злые силы препятствуют добрым намерениям архитектора. Например, в любой популярной среде разработки для Java или .NET, как только разработчик ссылается на класс, который еще не импортирован, IDE услужливо выводит диалоговое окно, в котором разработчику предлагается автоматически импортировать ссылку. Это происходит так часто, что у большинства программистов вырабатывается рефлекс отвечать согласием.
Однако произвольное импортирование классов или компонентов друг из друга означает катастрофу для модульности. Например, на рис. 1.1 показан особенно опасный антипаттерн, которого архитекторы стремятся избежать.
Рис. 1.1. Циклические зависимости между компонентами
В этом антипаттерне каждый компонент ссылается на два других. Такая сеть компонентов вредит модульности, поскольку разработчик не может повторно использовать один компонент, не привлекая другие. И конечно же, если другие компоненты связаны еще с какими-то, архитектура все больше и больше склоняется к антипаттерну Большой ком грязи (https://oreil.ly/usx7p). Могут ли архитекторы управлять этим поведением, не контролируя постоянно разработчиков, обожающих автоматический импорт зависимостей? Обзоры кода могут помочь, но выполняются слишком поздно в цикле разработки, чтобы быть эффективной мерой. Если архитектор позволит команде разработчиков безудержно импортировать зависимости из кодовой базы в течение недели до следующего обзора кода, то базе будет нанесен серьезный ущерб.
Решение этой проблемы состоит в том, чтобы написать функцию пригодности, проверяющую наличие циклических зависимостей, как показано в примере 1.1.
Пример 1.1. Функция пригодности для определения циклических зависимостей между компонентами
public class CycleTest {
private JDepend jdepend;
@BeforeEach
void init() {
jdepend = new JDepend();
jdepend.addDirectory("/path/to/project/persistence/classes");
jdepend.addDirectory("/path/to/project/web/classes");
jdepend.addDirectory("/path/to/project/thirdpartyjars");
}
@Test
void testAllPackages() {
Collection packages = jdepend.analyze();
assertEquals("Cycles exist", false, jdepend.containsCycles());
}
}
Здесь архитектор использовал инструмент метрик JDepend (https://oreil.ly/ozzzk), проверяющий зависимости между пакетами. Инструмент исследует структуру пакетов Java, и тест терпит неудачу, если обнаружатся какие-либо циклические зависимости. Архитектор может включить этот тест в процесс непрерывной сборки проекта и перестать беспокоиться о том, что разработчики случайно создадут циклы. Это отличный пример функции пригодности, охраняющей значимые методы разработки программного обеспечения: это важная задача для архитекторов, но она мало влияет на повседневную практику.
В примере 1.1 показана весьма низкоуровневая функция пригодности, ориентированная на код. Есть множество популярных инструментов гигиены кода (таких как SonarQube (https://www.sonarqube.org/)), которые реализуют многие типовые функции пригодности «под ключ». Однако архитектору может понадобиться проверить также макроструктуру архитектуры. Проектируя многоуровневую архитектуру (такую как на рис. 1.2), архитектор определяет уровни, чтобы обеспечить разделение задач.
Рис. 1.2. Традиционная многоуровневая архитектура
Как архитектор сможет проверить, что разработчики соблюдают границы уровней? Одни разработчики могут не понимать важности паттернов, тогда как другие могут занимать позицию «лучше попросить прощения, чем разрешения», стремясь поскорее решить какую-то локальную задачу, например повысить производительность. Но если разработчикам позволено нарушать границы, то долгосрочному состоянию архитектуры наносится вред.
ArchUnit (https://www.archunit.org/) позволяет архитекторам решить эту проблему с помощью функции пригодности, показанной в примере 1.2.
Пример 1.2. Функция ArchUnit, служащая для проверки соблюдения границ уровней
layeredArchitecture()
.layer("Controller").definedBy("..controller..")
.layer("Service").definedBy("..service..")
.layer("Persistence").definedBy("..persistence..")
.whereLayer("Controller").mayNotBeAccessedByAnyLayer()
.whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
.whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service")
В примере 1.2 архитектор определяет желательную взаимосвязь между уровнями и пишет функцию пригодности, предназначенную для проверки. Это позволяет архитектору устанавливать принципы архитектуры, помимо диаграмм и других информационных артефактов, и постоянно проверять их.
Аналогичный инструмент в пространстве .NET, NetArchTest (https://oreil.ly/EMXpv), дает возможность выполнять такие же проверки для этой платформы. Проверка границ уровней в C# показана в примере 1.3.
Пример 1.3. Использование NetArchTest для проверки границ уровней
// Классы, определенные на уровне представления,
// не должны напрямую обращаться к репозиториям
var result = Types.InCurrentDomain()
.That()
.ResideInNamespace("NetArchTest.SampleLibrary.Presentation")
.ShouldNot()
.HaveDependencyOn("NetArchTest.SampleLibrary.Data")
.GetResult()
.IsSuccessful;
В данном пространстве постоянно появляются все более сложные инструменты. Мы продолжим освещать эти методы, иллюстрируя функции пригодности наряду со многими нашими решениями.
Поиск объективного результата для функции пригодности имеет решающее значение. Однако объективность не означает статичность. Одни функции пригодности будут иметь неконтекстные возвращаемые значения, такие как true/false, или числовые значения, такие как порог производительности. Другие (считающиеся динамическими) будут возвращать значения, основанные на некоем контексте. Например, при измерении масштабируемости архитекторы оценивают количество одновременно работающих пользователей, а также производительность для каждого пользователя. Часто архитекторы проектируют системы так, чтобы при увеличении количества пользователей производительность на одного пользователя немного снижалась, но не падала резко. Для таких систем архитекторы разрабатывают функции пригодности, оценивающие производительность, которые учитывают количество одновременно работающих пользователей. Пока оценка архитектурной характеристики объективна, архитекторы могут ее тестировать.
Применение большинства функций пригодности можно и нужно автоматизировать, чтобы их можно было выполнять постоянно. Но будут и те, которые придется применять вручную. Такая функция пригодности требует, чтобы проверкой занимался человек. Например, при работе с системой, оперирующей конфиденциальной юридической информацией, юристу в целях обеспечения безопасности может потребоваться проверить изменения в критических частях, а это нельзя автоматизировать. Большинство конвейеров развертывания поддерживают этапы ручной проверки, что позволяет командам использовать ручные функции пригодности. В идеале они должны запускаться настолько часто, насколько это возможно, — проверка, которая не выполняется, ничего не сможет проверить. Команды выполняют функции пригодности по запросу (редко) или в рамках процесса непрерывной интеграции (чаще всего). Чтобы в полной мере воспользоваться преимуществами проверок, которые производятся функциями пригодности, их следует запускать постоянно.
Итак, непрерывность важна, как было показано в примере использования функций пригодности. Рассмотрим следующий сценарий: что делает компания, когда в одной из сред разработки или в какой-то библиотеке, используемых предприятием, обнаруживается эксплойт «нулевого дня»? Как и в большинстве компаний, эксперты по безопасности просматривают проекты, чтобы найти проблемную версию фреймворка и выполнить обновление, но этот процесс редко автоматизируется и основывается на множестве ручных операций. Это не абстрактный вопрос; именно этот сценарий касался крупного финансового учреждения (см. ниже врезку «Утечка данных в Equifax»). Как и при управлении архитектурой, описанном ранее, в ходе ручных процессов возникают ошибки и упускаются детали.
Утечка данных в Equifax
Седьмого сентября 2017 года Equifax, крупнейшее в США агентство по проверке кредитоспособности, объявило об утечке данных. Проблема была связана со взломом популярного веб-фреймворка Struts в экосистеме Java (Apache Struts vCVE-2017-5638). Организация-разработчик опубликовала заявление об уязвимости и выпустила исправление 7 марта 2017 года. На следующий день Министерство внутренней безопасности связалось с Equifax и аналогичными компаниями, предупредив о проблеме, и 15 марта 2017 года они провели сканирование, в ходе которого обнаружились не все уязвимые системы. В результате критическое исправление не было применено ко многим старым системам до 29 июля 2017 года, когда эксперты по безопасности Equifax выявили вторжение, приведшее к утечке данных.
Представьте альтернативный мир, в котором каждый проект имеет конвейер развертывания, а у группы безопасности есть свой «слот» в конвейере развертывания каждой команды, где они могут развертывать функции пригодности. В большинстве случаев это будут рутинные проверки мер безопасности, такие как предотвращение хранения паролей в базах данных и аналогичные обычные задачи по управлению. Однако при появлении эксплойта «нулевого дня» наличие везде одного и того же механизма позволит команде безопасности вставить тест в каждый проект, который проверит наличие определенного фреймворка и номер его версии и при обнаружении опасной версии завершит сборку, отправив уведомление группе безопасности. Команды настраивают конвейеры развертывания так, чтобы они начинали работу при появлении любых изменений в экосистеме: коде, схеме базы данных, конфигурации развертывания и функциях пригодности. Это позволяет предприятиям повсеместно автоматизировать важные задачи управления.
Функции пригодности дают архитекторам массу преимуществ, не последним из которых является возможность снова заняться программированием! Архитекторы часто жалуются, что они больше не пишут код, но функции пригодности часто являются кодом! Создавая выполняемую спецификацию архитектуры, которую каждый сможет проверить в любое время, запустив сборку проекта, архитекторы должны хорошо понимать систему и ее текущее развитие. Все это пересекается с основной целью — не отставать от кода проекта по мере его роста.
Какими бы эффективными ни были функции пригодности, архитекторам следует избегать чрезмерного их использования. Архитекторы не должны объединяться в клику и уединяться в башне из слоновой кости, чтобы построить невероятно сложный, взаимосвязанный набор функций пригодности, вызывающих раздражение у разработчиков и команды. Архитекторы должны рассматривать это как способ создания выполняемого контрольного списка важных, но не срочных аспектов программных проектов. Многие проекты являются срочными, из-за чего некоторые важные принципы выпадают из внимания. Это частая причина появления технического долга: «Мы знаем, что это плохо, но исправим это позже», а позже так и не наступает. Воплощая правила, которые касаются качества кода, структуры и других мер предосторожности в функции пригодности и выполняются постоянно, архитекторы создают чек-лист (контрольный список) качества, который разработчики не смогут пропустить.
Несколько лет назад в превосходной книге Атула Гаванде (Atul Gawande (Picador)) The Checklist Manifesto4 освещалось использование чек-листов профессионалами: хирургами, пилотами гражданской авиации и представителями других профессий, которые обычно (а иногда в силу закона) используют контрольные списки как часть своей работы, но совсем не потому, что не знают своей работы или слишком забывчивы. Когда профессионалы выполняют одну и ту же задачу снова и снова, становится легко обмануть себя и случайно пропустить какое-то важное действие, а чек-листы предотвращают это. Функции пригодности являются чек-листом важных принципов, определяемых архитекторами, и запускаются как часть конвейера сборки, чтобы гарантировать, что разработчики случайно (или намеренно, из-за внешних обстоятельств, таких как нехватка времени) не пропустят их.
Мы будем использовать функции пригодности на протяжении всей книги, когда появится возможность проиллюстрировать управление архитектурным решением и первоначальным проектом.
Архитектура и проектирование: определения должны быть простыми
Архитекторы постоянно борются за понимание выстраивания архитектуры и проектирования как отдельных, но взаимосвязанных видов деятельности. Мы не хотим вдаваться в бесконечные споры о различиях, но в этой книге стремимся твердо придерживаться архитектурной стороны спектра по нескольким причинам.
Во-первых, архитекторы должны понимать основные принципы архитектуры, чтобы принимать эффективные решения. Например, выбирая между синхронными и асинхронными взаимодействиями, придется разрешить ряд компромиссов, прежде чем переходить к деталям реализации. В книге Fundamentals of Software Architecture5 авторы сформулировали второй закон архитектуры программного обеспечения: «почему» важнее, чем «как». В конечном итоге архитекторы должны понимать, как реализовать решения, но прежде им предстоит понять, почему один вариант лучше другого.
Во-вторых, сосредоточившись на концепциях архитектуры, можно избежать их многочисленных реализаций. Архитекторы могут реализовать асинхронные взаимодействия различными способами, но нам интереснее понять, почему выбор пал на эти взаимодействия, а детали реализации мы обсудим в другом месте.
В-третьих, если мы начнем с реализации всех упоминаемых вариантов, это будет самая объемная книга из когда-либо написанных. Сосредоточившись на принципах архитектуры, мы сможем вести максимально общее обсуждение.
Чтобы быть как можно ближе к архитектуре, мы используем самые простые определения ключевых понятий. Например, связанности в архитектуре можно посвятить целые книги (и такие книги действительно были написаны). Ниже представлены упрощенные определения, которые мы используем.
• Сервис. Говоря простым языком, сервис — это связанный набор функций, развернутых в виде независимого выполняемого файла. Большинство концепций, которые в нашей дискуссии имеют отношение к сервисам, широко применяются к распределенным архитектурам, особенно к архитектурам микросервисов.
В терминах, определяемых в главе 2, сервис является частью архитектурного кванта, включающего дополнительные определения статической и динамической связи между сервисами и другими квантами.
• Связанность. Два артефакта (например, сервисы) считаются связанными, если изменение одного может потребовать изменения другого в целях поддержания надлежащей функциональности.
• Компонент. Архитектурный строительный блок приложения, решающий какую-либо бизнес- или инфраструктурную задачу, обычно проявляющуюся через структуру пакета (Java), пространство имен (C#) или физическую группировку файлов исходного кода в некой структуре каталогов. Например, компонент «История заказов» может быть реализован как набор классов в пространстве имен app.business.order.history.
• Синхронные взаимодействия. Два артефакта взаимодействуют синхронно, если вызывающая сторона должна дождаться ответа, прежде чем продолжить.
• Асинхронные взаимодействия. Два артефакта взаимодействуют асинхронно, если вызывающая сторона не ждет ответа, прежде чем продолжить. При необходимости ее можно уведомить о завершении обработки запроса, используя отдельный канал.
• Организованная (оркестрованная) координация. Рабочий процесс является организованным, если он включает сервис, основная обязанность которого — координация рабочего процесса.
• Хореографическая координация. Применяется, когда рабочему процессу не хватает организованной координации; часто выражается в создании нескольких сервисов в рабочем процессе, разделяющих обязанности по его координации.
• Атомарность. Рабочий процесс является атомарным, если все его части постоянно поддерживаются в согласованном состоянии; противоположность атомарности — спектр разных видов согласованности в конечном итоге (потенциальной согласованности), которые рассматриваются в главе 6.
• Контракт. Мы используем термин «контракт» в широком смысле, чтобы определить интерфейс между двумя частями программного обеспечения, который может определять вызовы методов или функций, удаленные вызовы интеграционной архитектуры, зависимости и т.д. Контракты имеют место везде, где соединяются две части ПО.
Архитектура программного обеспечения по своей природе абстрактна: мы не можем знать, какая уникальная комбинация платформ, технологий, коммерческого программного обеспечения и другого головокружительного набора возможностей может быть у наших читателей. Мы уверены лишь в том, что нет двух абсолютно одинаковых комбинаций. Мы рассматриваем множество абстрактных идей, но должны обосновать их некоторыми деталями реализации, чтобы добавить конкретики. Для этого нам нужна была задача, на примере которой мы могли бы проиллюстрировать архитектурные концепции, и это навело нас на мысль рассказать историю о вымышленной команде специалистов в Sysops Squad.
Введение в сагу о Sysops Squad
Сага — древнескандинавское и древнеирландское народное эпическое сказание о богах и героях.
Словарь Ожегова
В этой книге мы обсуждаем ряд саг, как в прямом, так и в переносном смысле. Архитекторы заимствовали термин «сага», чтобы описать поведение транзакций в распределенных архитектурах (которые мы подробно рассмотрим в главе 12). Однако дискуссии об архитектуре имеют тенденцию становиться абстрактными, особенно при рассмотрении абстрактных проблем, таких как сложные компромиссы архитектуры. Чтобы помочь решить стоящую перед нами задачу и предоставить некий реальный контекст для обсуждаемых решений, мы начинаем сагу о Sysops Squad.
В каждой главе мы рассказываем новую сагу о Sysops Squad, чтобы проиллюстрировать описываемые методы и компромиссы. Во многих книгах по архитектуре программного обеспечения рассказывается о новых разработках, однако в существующих системах имеется множество реальных проблем. Поэтому наша история начинается с существующей архитектуры.
Penultimate Electronics — гигант электронной промышленности, имеющий множество розничных магазинов по всей стране. Покупая компьютеры, телевизоры, стереосистемы и другое электронное оборудование, клиенты могут приобрести абонентское обслуживание. При возникновении проблем специалисты, работающие с клиентами (команда Sysops Squad), приезжают к клиенту на дом (или в офис), чтобы устранить проблемы с электронным устройством.
Системой абонентского обслуживания Sysops Squad пользуются четыре основных категории пользователей.
• Администраторы сопровождают внутренних пользователей системы и ведут список экспертов, в котором указывается их квалификация, местоположение и признак доступности в данный момент. Администраторы также управляют всей обработкой счетов клиентов, использующих систему, и сопровождают статические справочные данные (например, поддерживаемые продукты, пары «ключ — значение» в системе и т.д.).
• Клиенты регистрируются в сервисе Sysops Squad и получают свой профиль, контракт на поддержку и информацию для оплаты счетов. Клиенты сообщают о проблемах, вводя заявки в систему, а также оставляют замечания после выполнения работ.
• Эксперты Sysops Squad получают заявки и решают описанные в них проблемы. Кроме того, взаимодействуют с базой знаний, чтобы найти решения, и вносят информацию о проделанной работе.
• Менеджеры следят за обработкой заявок и получают оперативные и аналитические отчеты по всей системе заявок Sysops Squad.
Рабочий процесс, не связанный с заявками
Рабочий процесс, не связанный с заявками, включает действия, которые выполняются администраторами, менеджерами и клиентами, и не связан с решением проблем. Этот рабочий процесс можно описать так.
1. Администратор добавляет экспертов Sysops Squad в систему и сопровождает их, указывает их местонахождение, признак доступности и квалификацию.
2. Клиенты регистрируются в системе Sysops Squad и получают несколько вариантов абонентского обслуживания в зависимости от приобретенных продуктов.
3. Клиентам ежемесячно начисляются платежи на основе информации о кредитных картах в их профилях. Клиенты могут просматривать историю платежей и выписки.
4. Менеджеры запрашивают и получают различные оперативные и аналитические отчеты, в том числе финансовые, отчеты экспертов о проделанной работе и отчеты о заявках.
Рабочий процесс обработки заявок
Рабочий процесс обработки заявок начинается с момента, когда клиент вводит заявку в систему, и заканчивается, когда клиент заполняет отзыв по завершении ремонта. Этот рабочий процесс выглядит следующим образом.
1. Клиенты, приобретшие абонентское обслуживание, оставляют сообщение о проблеме на сайте Sysops Squad.
2. Когда заявка будет введена, система определяет, какой эксперт Sysops Squad лучше всего подходит для выполнения работ, исходя из квалификации, текущего местоположения, зоны обслуживания и доступности.
3. После назначения заявка выгружается в специальное приложение на мобильном устройстве эксперта Sysops Squad. Кроме того, эксперт получает текстовое сообщение о том, что за ним закреплена заявка.
4. Клиент получает текстовое SMS или сообщение по электронной почте (в зависимости от настроенных предпочтений в его профиле), что эксперт уже в пути.
5. Эксперт использует специальное мобильное приложение на своем телефоне, чтобы получить информацию о заявке и местоположении клиента. Кроме того, эксперт может получить доступ к базе знаний через мобильное приложение, чтобы узнать, как подобные проблемы решались в прошлом.
6. Решив проблему, эксперт помечает заявку как выполненную и при необходимости добавляет описание решения в базу знаний.
7. Получив уведомление о выполнении работ по заявке, система посылает клиенту электронное письмо со ссылкой на отзыв, который клиент должен будет заполнить.
8. Система получает заполненный отзыв от клиента и сохраняет его.
Плохой сценарий
В последнее время приложение приема и обработки заявок Sysops Squad работает не слишком хорошо. Текущая система заявок представляет собой большое монолитное приложение, разработанное много лет назад. Клиенты жалуются, что обслуживание не осуществляется из-за потери заявок и нередко для устранения проблем приезжает эксперт, не имеющий нужной квалификации. Кроме того, система не всегда доступна для ввода новых заявок.
Вносить изменения в такой большой монолит сложно и рискованно. Часто на это требуется слишком много времени, и нередко эти изменения приводят к нарушениям в работе в других частях приложения. Из-за проблем с надежностью система Sysops Squad часто зависает или дает сбой, в результате чего все функции приложения оказываются недоступными от пяти минут до двух часов, пока специалисты не определят источник проблемы, внесут исправления и перезапустят приложение.
Если не предпринять решительных мер в самое ближайшее время, то Penultimate Electronics будет вынуждена отказаться от очень прибыльной бизнес-линии контрактов на поддержку и уволить всех администраторов Sysops Squad, экспертов, менеджеров и IT-разработчиков, включая архитекторов.
Архитектурные компоненты Sysops Squad
Монолитное приложение Sysops Squad обрабатывает заявки, генерирует операционную отчетность, регистрирует клиентов и выставляет счета, а также осуществляет общие административные функции, такие как сопровождение учетных записей и профилей пользователей и экспертов. Компоненты существующего монолитного приложения (часть ss. в названии пространства имен определяет контекст приложения Sysops Squad) показаны на рис. 1.3 и описаны в табл. 1.1.
Рис. 1.3. Компоненты существующего приложения Sysops Squad
Таблица 1.1. Компоненты существующего приложения Sysops Squad
| Компонент |
Пространство имен |
Описание |
| Вход |
ss.login |
Учетные записи внутренних пользователей и клиентов и логика обеспечения безопасности |
| Платежи за обслуживание |
ss.billing.payment |
Счет клиента для оплаты услуг и информация о кредитной карте |
| История платежей |
ss.billing.history |
История платежей и платежные квитанции за предыдущие периоды |
| Уведомление клиентов |
ss.customer.notification |
Уведомление клиента о выставлении счета, общая информация |
| Профили клиентов |
ss.customer.profile |
Поддержка профилей клиентов, регистрация новых клиентов |
| Профили экспертов |
ss.expert.profile |
Поддержка профилей экспертов (имя, местонахождение, квалификация и т.д.) |
| Поддержка БЗ |
ss.kb.maintenance |
Ведение базы знаний и записей в ней |
| Поиск в БЗ |
ss.kb.search |
Механизм обработки запросов для поиска в базе знаний |
| Отчетность |
ss.reporting |
Все отчеты (об экспертах, заявках, платежах) |
| Заявки |
ss.ticket |
Создание заявки, сопровождение, завершение, общий код |
| Назначение заявок |
ss.ticket.assign |
Поиск эксперта и передача ему заявки |
| Уведомления о приеме заявок |
ss.ticket.notify |
Рассылка клиентам уведомлений о том, что эксперт выехал |
| Маршрутизация заявок |
ss.ticket.route |
Отправка заявок в мобильные приложения экспертов |
| Сопровождение контрактов |
ss.supportcontract |
Сопровождение контрактов для клиентов, приобретенные продукты, включенные в абонентское обслуживание |
| Отзывы |
ss.survey |
Поддержка отзывов, сбор и запись отзывов |
| Запросы на отзывы |
ss.survey.notify |
Отправка клиентам по электронной почте предложений оставить отзыв |
| Шаблоны отзывов |
ss.survey.templates |
Сопровождение различных отзывов в зависимости от типа услуги |
| Сопровождение пользователей |
ss.users |
Сопровождение внутренних пользователей и ролей |
Мы будем использовать эти компоненты в последующих главах, чтобы показать различные методы и компромиссы, применяемые и принимаемые при преобразовании приложений в распределенные архитектуры.
Модель данных Sysops Squad
Приложение Sysops Squad с его различными компонентами, перечисленными в табл. 1.1, использует единую схему в базе данных, чтобы разместить все свои таблицы и соответствующий код БД. База данных применяется для хранения информации о клиентах, пользователях, контрактах, счетах, платежах, базы знаний и отзывов клиентов; соответствующие таблицы перечислены в табл. 1.2, а модель ER показана на рис. 1.4.
Таблица 1.2. Таблицы базы данных для существующего приложения Sysops Squad
| Таблица |
Описание |
| Customer |
Клиенты, обслуживаемые в приложении Sysops |
| Customer_Notification |
Настройки уведомлений для клиентов |
| Survey |
Отзывы клиентов после оказания услуги |
| Question |
Вопросы в отзыве |
| Survey_Question |
Вопрос в запросе на отзыв |
| Survey_Administered |
Вопрос, заданный клиенту в запросе на отзыв |
| Survey_Response |
Ответ клиента на вопросы в отзыве |
| Billing |
Платежная информация для контракта на поддержку |
| Contract |
Контракт между клиентом и Sysops |
| Payment_Method |
Поддерживаемые способы оплаты |
| Payment |
Обработанные платежи |
| SysOps_User |
Различные пользователи в Sysops |
| Profile |
Информация из профилей пользователей Sysops |
| Expert_Profile |
Профили экспертов |
| Expertise |
Различные квалификации в Sysops |
| Location |
Районы, обслуживаемые экспертами |
| Article |
Статьи в базе знаний |
| Tag |
Теги в статьях |
| Keyword |
Ключевые слова в статьях |
| Article_Tag |
Теги, связанные со статьями |
| Article_Keyword |
Ключевые слова, связанные со статьями |
| Ticket |
Заявки, оставленные клиентами |
| Ticket_Type |
Различные типы заявок |
| Ticket_History |
История обслуживания заявок |
Рис. 1.4. Модель данных в существующем приложении Sysops Squad
Модель данных Sysops — стандартная модель данных, имеющая третью нормальную форму и несколько хранимых процедур и триггеров. Однако существует довольно много представлений, которые в основном используются компонентом «Отчетность». Цель команды архитекторов — разбить приложение и перейти к распределенной архитектуре, поэтому им придется работать с командой базы данных, чтобы организовать выполнение задач на уровне базы. Мы будем использовать эту организацию таблиц и представлений в базе данных на протяжении всей книги, чтобы обсуждать различные методы и компромиссы, которые применяются и принимаются в целях решения задачи разделения базы данных.
2 Форд Н., Ричардс М. Фундаментальный подход к программной архитектуре.
3 Форд Н., Парсонс Р., Куа П. Эволюционная архитектура. Поддержка непрерывных изменений. — СПб.: Питер, 2022.
4 Гаванде А. Чек-лист. Как избежать глупых ошибок, ведущих к фатальным последствиям.
5 Форд Н., Ричардс М. Фундаментальный подход к программной архитектуре.
Использование функций пригодности. В книге Building Evolutionary Architectures3 (O’Reilly) 2017 года авторы (Нил Форд, Ребекка Парсонс и Патрик Куа) определили концепцию функции пригодности для архитектуры: любой механизм, объективно оценивающий целостность некой архитектурной характеристики или их комбинации. Коротко разберем это определение по пунктам.
Форд Н., Ричардс М. Фундаментальный подход к программной архитектуре.
Гаванде А. Чек-лист. Как избежать глупых ошибок, ведущих к фатальным последствиям.
Форд Н., Парсонс Р., Куа П. Эволюционная архитектура. Поддержка непрерывных изменений. — СПб.: Питер, 2022.
Форд Н., Ричардс М. Фундаментальный подход к программной архитектуре.
Несколько лет назад в превосходной книге Атула Гаванде (Atul Gawande (Picador)) The Checklist Manifesto4 освещалось использование чек-листов профессионалами: хирургами, пилотами гражданской авиации и представителями других профессий, которые обычно (а иногда в силу закона) используют контрольные списки как часть своей работы, но совсем не потому, что не знают своей работы или слишком забывчивы. Когда профессионалы выполняют одну и ту же задачу снова и снова, становится легко обмануть себя и случайно пропустить какое-то важное действие, а чек-листы предотвращают это. Функции пригодности являются чек-листом важных принципов, определяемых архитекторами, и запускаются как часть конвейера сборки, чтобы гарантировать, что разработчики случайно (или намеренно, из-за внешних обстоятельств, таких как нехватка времени) не пропустят их.
Один из наиболее эффективных способов документирования архитектурных решений — запись в реестре архитектурных решений (Architectural Decision Records, ADR (https://adr.github.io/)). Впервые использовать ADR предложил Майкл Найгард (Michael Nygard) в своей статье в блоге (https://oreil.ly/yDcU2), а затем они были отмечены как «принятые» в сборнике трендов/технологий Thoughtworks Technology Radar (https://oreil.ly/0nwHw). ADR состоит из короткого текстового файла (обычно одна-две страницы), описывающего конкретное архитектурное решение. ADR могут быть оформлены как обычные текстовые файлы, но чаще для их оформления применяется какой-либо текстовый формат, такой как AsciiDoc (http://asciidoc.org/) или Markdown (https://www.markdownguide.org/). Кроме того, запись ADR можно оформить с помощью шаблона вики-страницы. Мы посвятили ADR целую главу в нашей предыдущей книге Fundamentals of Software Architecture2 (https://learning.oreilly.com/library/view/fundamentals-of-software/9781492043447).
Во-первых, архитекторы должны понимать основные принципы архитектуры, чтобы принимать эффективные решения. Например, выбирая между синхронными и асинхронными взаимодействиями, придется разрешить ряд компромиссов, прежде чем переходить к деталям реализации. В книге Fundamentals of Software Architecture5 авторы сформулировали второй закон архитектуры программного обеспечения: «почему» важнее, чем «как». В конечном итоге архитекторы должны понимать, как реализовать решения, но прежде им предстоит понять, почему один вариант лучше другого.
Часть I. Разделение компонентов
Многие из нас еще в детстве усвоили, что лучший способ понять, как что-то соединяется, — сначала разобрать это на части. Чтобы разобраться в сложных вопросах (таких как компромиссы в распределенных архитектурах), архитектор должен понять, с чего начать распутывание (untangling)6.
В книге What Every Programmer Should Know About Object-Oriented Design (Dorset House; https://oreil.ly/bLPm4) Мейлир Пейдж-Джонс (Meilir Page-Jones) сделал проницательное наблюдение, что связанность в архитектуре можно разделить на статическую и динамическую. Статическая связанность (static coupling) определяет, как архитектурные части (классы, компоненты, сервисы и т.д.) связаны друг с другом: зависимости, степень связанности, точки сопряжения и т.д. Архитектор часто может измерить статическую связанность во время компиляции, поскольку она представляет статические зависимости в архитектуре.
Динамическая связанность (dynamic coupling) определяет, как архитектурные части вызывают друг друга: как осуществляются взаимодействия, какая информация передается, строгость контрактов и т.д.
Наша цель — исследовать порядок анализа компромиссов в распределенных архитектурах; для этого мы должны разделить движущиеся части, чтобы обсудить их по отдельности и полностью понять их устройство, прежде чем собирать обратно.
Часть I главным образом посвящена архитектурной структуре — статическим связям между компонентами. В главе 2 мы решим задачу определения влияния статической и динамической связанности в архитектурах и представим полную картину, которую должны разобрать, чтобы понять ее. Глава 3 начинает этот процесс, определяя модульность и границы разделения в архитектуре. В главе 4 будут представлены инструменты для оценки и разделения базы кода, а в главе 5 — паттерны, помогающие в этом процессе.
Важность данных и транзакций в архитектуре постоянно растет, вследствие чего архитекторы и администраторы баз данных должны принимать множество компромиссных решений. Глава 6 посвящена влиянию данных на архитектуру и, помимо всего прочего, рассказывает, как согласовать границы сервисов и данных. Наконец, в главе 7 мы свяжем архитектуру с проблемами данных, чтобы определить силы интеграции и дезинтеграции, которые способствуют увеличению или уменьшению размеров сервисов.
6 Употребляя далее термин «архитектурный квант», авторы проводят параллели с квантовой механикой и, в частности, с квантовой запутанностью (это явление, при котором квантовые состояния двух или большего числа объектов оказываются взаимозависимыми). Отсюда употребление в переводе слова «распутывание». Можно интерпретировать как распутывание запутанного клубка компромиссов. — Примеч. пер.
Многие из нас еще в детстве усвоили, что лучший способ понять, как что-то соединяется, — сначала разобрать это на части. Чтобы разобраться в сложных вопросах (таких как компромиссы в распределенных архитектурах), архитектор должен понять, с чего начать распутывание (untangling)6.
Употребляя далее термин «архитектурный квант», авторы проводят параллели с квантовой механикой и, в частности, с квантовой запутанностью (это явление, при котором квантовые состояния двух или большего числа объектов оказываются взаимозависимыми). Отсюда употребление в переводе слова «распутывание». Можно интерпретировать как распутывание запутанного клубка компромиссов. — Примеч. пер.
Глава 2. Выявление связей в архитектуре программного обеспечения
Среда, 3 ноября, 13:00
Логан, ведущий архитектор Penultimate Electronics, вмешался в разговор небольшой группы архитекторов, обсуждавших распределенные архитектуры в кафе:
— Остин, ты опять в гипсе?
— Нет, это просто шина, — ответил Остин. — Я растянул запястье, играя в экстремальный диск-гольф на выходных, — оно почти зажило.
— Что это... а впрочем, неважно. Что это за жаркий спор, в который я вмешался?
— Мы обсуждали, стоит ли постоянно выбирать паттерн саги в микросервисных архитектурах, чтобы объединять транзакции, — ответил Остин. — Следуя этой идее, архитекторы могут делать сервисы настолько маленькими, насколько захотят.
— Но разве вместе с сагами не нужно использовать оркестрацию? — спросил Эддисон. — А как быть в случаях, когда взаимодействия должны выполняться асинхронно? И насколько сложными станут транзакции? Если разбить все на слишком мелкие части, то сможем ли мы гарантировать точность данных?
— Знаете, — ответил Остин, — если использовать сервисную шину предприятия (Enterprise Service Bus, ESB), то можно заставить ее управлять всем этим автоматически.
— Я думал, что никто больше не использует ESB. Не лучше ли использовать Kafka?
— Но они даже не одно и то же! — сказал Остин.
Логан прервал разгорающийся спор:
— Это сравнение яблок с апельсинами! Ни один из этих инструментов или подходов не является панацеей. Распределенные архитектуры, такие как микросервисы, сложны, особенно если архитекторы не могут распутать все взаимозависимости. Нам нужны подход или структура, которые помогут разобраться в сложных проблемах нашей архитектуры.
— Что ж, — сказал Эддисон, — в любом случае компоненты должны быть максимально изолированными друг от друга — во всех публикациях, которые я читал, говорится, что архитекторы должны максимально использовать разделение.
— Если вы последуете этому совету, — сказал Логан, — то компоненты окажутся настолько изолированными, что не смогут взаимодействовать между собой, — писать подобные программы очень, очень сложно! Связанность, как и многое другое, по своей сути не является чем-то плохим; архитекторы просто должны знать, как ее применять. Я помню известную цитату одного швейцарского философа...
Всё есть яд, и нет ничего неядовитого; одна лишь дозировка превращает яд в лекарство.
Парацельс
Одна из самых сложных задач, с которыми сталкиваются архитекторы, — распутывание различных взаимозависимостей и компромиссов, действующих в распределенных архитектурах. Люди, дающие советы, постоянно превозносят преимущества слабо связанных систем, но можно ли проектировать системы, в которых ничто ни с чем не связано? Архитекторы проектируют создание небольших микросервисов, чтобы добиться разделения, но в таких архитектурах оркестрация, управление транзакциями и асинхронность превращаются в огромные проблемы. Обобщенно рекомендуется «разделить», но не говорится, как сделать это, чтобы при этом получить полезные системы.
Архитекторы борются с проблемами излишне мелкого дробления и взаимодействий, поскольку не существует четких универсальных рецептов — зарекомендовавших себя решений, которые можно было бы применить к реальным сложным системам. До сих пор у архитекторов не было правильной точки зрения и терминологии, позволяющих провести тщательный анализ, чтобы определить наилучший (или наименее худший) набор компромиссов в каждом конкретном случае.
Почему архитекторам приходилось бороться с проблемами в распределенных архитектурах? В конце концов, распределенные системы создаются еще с прошлого века с помощью почти одних и тех же механизмов (очередей сообщений, событий и т.д.). Почему появление микросервисов настолько увеличило сложность?
Ответ кроется в самой философии микросервисов, основанной на идее ограниченного контекста. Создание сервисов, моделирующих ограниченные контексты, потребовало внести незначительное, но важное изменение в способ проектирования распределенных систем, вследствие чего транзакционность превратилась в архитектурную проблему. Во многих распределенных системах, которые предполагается преобразовать в набор микросервисов, обработчики событий обычно подключаются к одной реляционной базе данных, что позволяет обеспечить целостность данных и поддержку транзакций. Перемещение базы данных в границы сервиса превращает проблемы данных в архитектурные проблемы.
Как уже говорилось выше, архитектура программного обеспечения — то, о чем не расскажет Google. Современные архитекторы должны развивать в себе способность анализировать компромиссы. Конечно, есть несколько фреймворков, существующих уже не одно десятилетие (например, метод анализа компромиссов архитектуры — Architecture Trade-off Analysis Method, ATAM (https://oreil.ly/okbuO)), но они не уделяют достаточно внимания реальным проблемам, с которыми архитекторы сталкиваются ежедневно.
Эта книга рассказывает о том, как архитекторы могут анализировать компромиссы в самых разных сценариях, уникальных для их ситуации. Как и во многом другом в архитектуре, совет прост: самое трудное кроется в деталях, особенно в их сложных переплетениях, что затрудняет обзор и понимание отдельных частей, как показано на рис. 2.1.
Рис. 2.1. Волосы заплетаются в косу, из-за чего отдельные пряди трудно различить
Когда архитекторы рассматривают запутанные проблемы, им сложно анализировать компромиссы, поскольку трудно разделять задачи в целях их рассмотрения независимо друг от друга. Поэтому первый шаг в анализе компромиссов — распутывание проблем, анализ взаимосвязей между частями и определение, как эта взаимосвязанность влияет на изменения. Для этого воспользуемся простейшим определением слова «связанность» (coupling).
Две части программного обеспечения считаются связанными, если изменение одной может потребовать изменения другой.
Часто архитектура ПО создает многомерные проблемы, когда множество факторов действует взаимозависимым образом. Чтобы проанализировать компромиссы, архитектор должен сначала определить, какие силы должны идти на компромисс друг с другом.
Итак, наш совет относительно современного анализа компромиссов в архитектуре ПО звучит так.
1. Найдите части, перепутанные между собой.
2. Проанализируйте, как они связаны друг с другом.
3. Оцените компромиссы, определив влияние изменений на взаимозависимые системы.
Эти шаги кажутся простыми, но не забывайте, что сложности скрываются в деталях. Чтобы проиллюстрировать это на практике, возьмем одну из самых сложных (и, пожалуй, наиболее общих) проблем в распределенных архитектурах, которая связана с микросервисами:
Как архитекторы определяют размеры и стили взаимодействий микросервисов?
Определение надлежащего размера микросервисов кажется распространенной проблемой: слишком маленькие сервисы создают проблемы с транзакциями и оркестрацией, а слишком большие — с масштабированием и распределением.
В оставшейся части этой книги мы распутаем множество аспектов, которые необходимо учитывать при ответе на предыдущий вопрос, предложим новую терминологию для различения похожих, но все же разных паттернов и покажем практические примеры применения этих и других паттернов.
Однако главная цель книги — представить и продемонстрировать на примерах методы, которые помогут вам научиться самостоятельно анализировать компромиссы при решении уникальных проблем в своей области. Итак, начнем наше первое великое распутывание сил в распределенных архитектурах: определим квант архитектуры и два типа связанности, статическую и динамическую.
Архитектурные кванты
Термин «квант» широко используется в таком разделе физики, как квантовая механика. И мы выбрали это слово по тем же причинам, что и физики. «Квант» происходит от латинского слова quantus, означающего «сколь велик» или «сколь много». До заимствования физиками это слово использовалось в юриспруденции для представления «требуемой или допустимой суммы» (например, в качестве возмещения убытков). Этот термин также используется в топологии — разделе математики, изучающем свойства семейств фигур.
В архитектуре квант измеряет несколько аспектов, которые относятся к топологии и поведению ПО и связаны с особенностями соединения частей и их взаимодействий друг с другом:
• архитектурный квант — это независимо развертываемый артефакт с высокой функциональной связностью (cohesion)7, высокой статической связанностью (coupling) и синхронной динамической связанностью. Типичный пример архитектурного кванта — правильно сформированный микросервис в рабочем процессе;
• статическая связанность определяется статическими зависимостями, разрешаемыми в архитектуре с помощью контрактов. К числу таких зависимостей относятся операционная система, фреймворки и/или библиотеки, необходимые для транзитивного разрешения зависимостей, а также любые другие операционные требования, необходимые для работы кванта;
• динамическая связанность определяет, как кванты взаимодействуют во время выполнения, синхронно или асинхронно. Следовательно, функции пригодности, проверяющие эти характеристики, должны выполняться непрерывно, например, с помощью мониторов.
Статическая и динамическая связанность кажутся похожими, однако архитекторы должны помнить о двух важных различиях: статическая связанность описывает, как сервисы переплетены друг с другом, а динамическая — как сервисы вызывают друг друга во время выполнения. Например, в архитектуре микросервисов сервис должен содержать зависимые компоненты, такие как база данных, представляющие статическую связанность, — без необходимых данных сервис не сможет работать. Этот сервис может вызывать другие сервисы в процессе выполнения, и эти вызовы образуют динамическую связанность. Для работы ни одного из сервисов не требуется присутствия другого, за исключением данного рабочего процесса. Таким образом, статическая связанность определяет операционные зависимости, а динамическая — коммуникационные.
Эти определения включают важные характеристики; подробно рассмотрим каждую из них, так как они используются во многих примерах в книге.
Возможность независимого развертывания
Возможность независимого развертывания подразумевает несколько аспектов архитектурного кванта — каждый квант представляет собой модуль, развертываемый отдельно в рамках конкретной архитектуры. Соответственно, монолитная архитектура, развернутая как единое целое, по определению представляет собой единый архитектурный квант. В распределенной архитектуре, например в микросервисной, разработчики стремятся развертывать сервисы независимо, часто прибегая к использованию автоматизированных конвейеров. Таким образом, с точки зрения независимого развертывания сервис в архитектуре микросервисов — это архитектурный квант (в зависимости от связанности, как будет показано далее).
Превращение каждого архитектурного кванта в развертываемый ресурс служит нескольким полезным целям. Во-первых, граница, представленная архитектурным квантом, служит полезным общим языком для архитекторов, разработчиков и операторов. Каждый понимает общую область применения: архитекторы понимают характеристики связанности, разработчики — функциональную область, а операторы — характеристики развертывания.
Во-вторых, архитектурный квант — одна из сил (статическая связанность), которую архитекторы должны учитывать, стремясь получить надлежащее разделение на сервисы в распределенной архитектуре. Часто в микросервисных архитектурах разработчики сталкиваются со сложной проблемой выбора степени детализации сервисов, дающей оптимальный набор компромиссов. Некоторые из этих компромиссов касаются возможности развертывания: как часто должны выпускаться новые версии этого сервиса, на какие другие сервисы может быть оказано влияние, какие инженерные методы задействованы и т.д. Архитекторам выгодно четко понимать, где именно проходят границы развертывания в распределенных архитектурах. Детализацию сервисов и связанные с этим компромиссы мы обсудим в главе 7.
В-третьих, возможность независимого развертывания заставляет архитектурный квант включать в себя общие точки сопряжения, такие как базы данных. В большинстве дискуссий об архитектуре ради удобства игнорируются такие вопросы, как базы данных и пользовательские интерфейсы, но эти проблемы часто приходится решать в реальных системах. Соответственно, любая система, использующая общую базу данных, не удовлетворяет критериям квантования архитектуры с точки зрения независимости развертывания, если только развертывание базы данных не является обязательным этапом развертывания приложения. Многие распределенные системы, которые иначе могли бы состоять из множества квантов, терпят неудачу в организации независимого развертывания из-за того, что используют общую базу данных, имеющую собственную частоту развертывания. Таким образом, анализ одних лишь границ развертывания не является исключительно полезной мерой. Чтобы провести разумные границы архитектурного кванта, архитекторы должны также учитывать второй критерий — высокую функциональную связность.
Высокая функциональная связность
Под высокой функциональной связностью (high functional cohesion) подразумевается близость связанных элементов: классов, компонентов, сервисов и т.д. В ходе исторического развития ученые-информатики определяли множество типов связности, ограничиваясь в данном случае понятием модуля, который может быть представлен в виде класса или компонента, в зависимости от платформы. С точки зрения предметной области техническое определение высокой функциональной связности пересекается с целями ограниченного контекста в предметно-ориентированном проектировании: код и данные, реализующие рабочий процесс в конкретной предметной области.
С точки зрения возможности независимого развертывания гигантская монолитная архитектура квалифицируется как архитектурный квант. Тем не менее она почти наверняка не отличается высокой функциональной связностью и скорее лишь включает в себя функциональность всей системы. Чем больше монолит, тем ниже вероятность, что он действительно имеет высокую функциональную связность.
В идеале в архитектуре микросервисов каждый сервис моделирует одну предметную область или рабочий процесс и, следовательно, демонстрирует высокую функциональную связность. Связность в этом контексте заключается не в том, как сервисы взаимодействуют для выполнения работы, а скорее в том, насколько они независимы и связаны друг с другом.
Тесная статическая связанность
Тесная статическая связанность подразумевает тесную связь элементов внутри архитектурного кванта, что на самом деле является аспектом контрактов. Архитекторы считают контрактами такие вещи, как REST или SOAP, но сигнатуры методов и операционные зависимости (через точки сопряжения, такие как IP-адреса или URL) тоже представляют собой контракты. Соответственно, контракты тоже являются сложным компромиссом архитектуры; вопросы связанности, имеющие отношение ко всем типам контрактов, в том числе выбор подходящих контрактов, мы рассмотрим в главе 13.
Архитектурный квант частично выступает мерой статической связанности, и эта мера довольно проста для большинства архитектурных топологий. Например, на следующих диаграммах показаны архитектурные стили, представленные в книге Fundamentals of Software Architecture8, иллюстрирующие статическую связанность в архитектурном кванте.
Любой из стилей монолитной архитектуры обязательно будет иметь один квант, как показано на рис. 2.2.
Рис. 2.2. Монолитные архитектуры сами являются квантами
Как видите, любая архитектура, развертываемая как единое целое и использующая единую базу данных, всегда будет иметь единственный квант. Квантовая мера статической связанности в архитектуре включает базу данных, а система, основанная на одной базе данных, не может иметь более одного кванта. Соответственно, квантовая мера статической связанности помогает определить точки сопряжения во всей архитектуре, а не только в разрабатываемых программных компонентах. Большинство монолитных архитектур содержат единственную точку сопряжения (как правило, базу данных), что делает ее квантовую меру равной единице.
Распределенные архитектуры часто имеют развязку на уровне компонентов; рассмотрим еще один набор архитектурных стилей, начав с сервисно-ориентированной архитектуры, показанной на рис. 2.3.
Рис. 2.3. Архитектурный квант сервисно-ориентированной архитектуры
Эта модель, состоящая из отдельных сервисов, демонстрирует изоляцию, характерную для микросервисов, однако сама архитектура по-прежнему использует единую реляционную базу данных, что делает ее квантовую меру равной единице.
Сервисно-ориентированная архитектура
Говоря о сервисно-ориентированной архитектуре, мы имеем в виду не архитектуру вообще, основанную на сервисах, а скорее особый гибридный архитектурный стиль, следующий распределенной многоуровневой структуре, которая состоит из отдельно развернутого пользовательского интерфейса, отдельно развернутых удаленных сервисов и монолитной базы данных. Эта архитектура решает одну из проблем, свойственных микросервисам, — разделение на уровне базы данных. Сервисы в сервисно-ориентированной архитектуре следуют тем же принципам, что и микросервисы (на основе ограниченного контекста в предметно-ориентированном проектировании), но полагаются на единую реляционную базу данных, поскольку архитекторы не видели смысла в ее разделении (или видели слишком много негативных компромиссов).
Сервисно-ориентированные архитектуры часто используются при реструктуризации монолитных архитектур, так как позволяют выполнить декомпозицию, не изменяя существующие схемы баз данных и точки интеграции. Подробнее о паттернах декомпозиции мы поговорим в главе 5.
До сих пор мера статической связанности архитектурного кванта оценивала все топологии как единичные. Однако распределенные архитектуры создают возможность появления множества квантов, хотя и не гарантируют этого. Например, модель, реализующая архитектурный стиль «Посредник» (Mediator) и управляемая событиями, всегда будет оцениваться в качестве единственного кванта, как показано на рис. 2.4.
