Привет! Меня зовут Алина, я работаю техническим писателем в компании Quadcode. В этой статье хочу поделиться опытом верхнеуровневого описания архитектуры системы с использованием структуры C4. Небольшая оговорка: предпринятые шаги включают в себя определенные отходы от канонической нотации в угоду удобству и особенностям системы.
Для справки:
HLD (high-level design) – верхнеуровневое описание архитектуры системы, где представлены основные компоненты и их взаимодействия.
LLD (low-level design) – низкоуровневое детальное описание каждого из компонентов системы.
Созданием HLD-документации предполагалось закрыть следующие потребности:
Собрать воедино информацию по устройству системы на bird’s-eye уровне для общего понимания.
Снизить порог вхождения в предметную область для новых сотрудников.
Иметь возможность предоставлять блоки HLD-документации по запросу регуляторов.
Особенности
Одной из самых непростых особенностей можно назвать солидный срок жизни платформы: к моменту старта работы она благополучно функционировала в течение 8 лет (и продолжает, собственно), что внесло коррективы в подход к описанию. Большинство нотаций говорят в первую очередь о проектировании, т.е. о схематичном общем устройстве системы, которую еще только предстоит реализовать. Здесь же было необходимо описать архитектуру вполне реального, сложносочиненного финтех-продукта, который за время своего существования успел обрасти рядом легаси, постоянно расширялся с бизнесовой точки зрения, а архитектура сменила парадигму своего существования и перешла с монолита на микросервисы.
Вытекающая из первой особенность номер два: какие домены выделить в системе на верхнем уровне? Что считать отдельным направлением, а что – зависимой частью? Критерии, на основе которых стоило выделить основные направления, в команде архитектуры отличались, к общему мнению еще только предстояло прийти.
Нотации
Прежде чем остановиться на C4, было проведено небольшое исследование общепринятых нотаций описания архитектуры системы. Среди основных можно выделить:
Модель 4+1
ANSI/IEEE 1471-2000
Представленные выше модели обладают общими чертами, к примеру, практически везде присутствует деление на уровни: Logical/Conceptual/Contextual, которые переходят на более низкий уровень, i.e. Domain/Process -> уровень Development.
Нотация C4 не является чем-то инновационным по сравнению с представленными выше структурами описания, но выглядит демократичнее.
C4
Немного общих слов про нотацию, поскольку про нее отдельно уже писали и не раз. Итак, C4 - один из способов визуализации архитектуры программного обеспечения. В названии кроется структура нотации: Context+Container+Component+Code=С4. Представлена широкой публике недавно, в 2018 году, является деривативом нотации 4+1. Подробное описание компонентов диаграмм всех уровней есть на github по ссылке. Для HLD-документации мы взяли первые два уровня - “контекст” и “контейнеры”. “Компоненты” и “код” логически больше подходили под LLD-направление, поэтому в скоуп задачи они не вошли.
Контекст
После определенных дискуссий было решено выделить основные домены платформы, которые обладали высоким уровнем самодостаточности, а именно:
Trading Platform: торговые инструменты, администрирование, биллинг, комплаенс, антифрод.
Affiliate Program: инструмент для работы с партнерами по привлечению новых пользователей на платформу.
Analytics: инструменты сбора и анализа данных на платформе, куда входят направления Data Platform, Data Science Dev, Insights&Experiments.
Transport Infrastructure: внутренняя транспортная система (шина данных), а также инструменты Retention, которые логически подпадают под определение транспорта сообщений.
Banking: инструменты по интеграции с платежными методами, проведение SWIFT/SEPA платежей, онлайн банкинг.
В рамках контекстной диаграммы каждый из доменов представляется единым элементом, который взаимодействует с внешними программами и/или провайдерами. В реальности такое отображение оказалось практически невозможным из-за большого количества внешних взаимодействий, поэтому компромиссным решением было выделение ряда связанных направлений, которые логически объединялись в одну общую границу домена. Подобная контекстная диаграмма сформировалась для транспортной инфраструктуры.
Кстати, границы могут быть вложенными:
Boundary(c1, "Transport Domains"){
Boundary(c2, "Transport Management System"){
System(transportAdmin, "Bus-kit", "Manages transport messages/permissions")
System(consul, "Consul", "Provides information on microservices")
}
System(internalTransport, "Internal Transport", "Transfers messages between microservices")
System(externalTransport, "External Transport", "Transfers messages to external clients")
}
Основные элементы диаграммы контекстного уровня:
System - логически определенный элемент, представляющий собой сервис или ряд сервисов. Поскольку контекст показывает самый верхний уровень, стек технологий и технические детали указывать необязательно.
System_external (не представлен на диаграмме) - внешний сервис, взаимодействующий с одним или несколькими элементами системы.
Boundary - граница, включающая в себя ряд логически объединенных элементов.
Контейнеры
За основные элементы диаграммы контейнеров было решено взять микросервисы, СУБД, а также SPA, landings, клиентские приложения для ряда случаев (как отправная точка). В зависимости от обширности домена количество контейнерных диаграмм может разниться. К примеру, в партнерской программе их 2, а в торговой платформе - 10. Если брать в качестве примера домен торговой платформы, логически диаграммы этого уровня были разделены на:
Торговые инструменты - Margin Forex, CFD, Options;
Compliance - GDPR, верификация пользователей, etc;
Backoffice - администрирование;
Antifraud - борьба с мошенничеством;
Billing - депозиты/выводы средств, подключение новых платежных методов;
Trading Activity - обучающие видео, финансовые календари и т.д.;
Retention/acquisition - инструменты по удержанию пользователей на платформе.
Самым обширным поддоменом из списка выше ожидаемо оказались торговые инструменты, поэтому для каждого из них была составлена отдельная диаграмма контейнеров.
Если у микросервиса, входящего в домен, есть своя база данных, ее необходимо отразить на схеме. Если компонент взаимодействует с большим количеством внешних по отношению к нему сервисов, для сохранения читабельности диаграммы можно объединить эти сервисы в логически определенный элемент, как, например, это было сделано с Product Microservices на части диаграммы ниже.
В контейнерной диаграмме также могут участвовать внешние сервисы, которые обозначаются элементом external_container, как это показано на части диаграммы ниже.
К элементам контекста на уровне контейнеров добавляются новые:
Container - логически определенный элемент, основная единица диаграммы;
ContainerDb - база данных того или иного контейнера или отдельно стоящая СУБД для ряда случаев (i.e. Cassandra).
Для более удобного расположения элементов диаграммы можно использовать направления ..._Right, _Left; _Down; _Up и т.д. при обозначении взаимодействия:
Rel_Right(paymentProcessing, paymentProcessingDb, "Reads, writes to")
Каждую диаграмму на обоих уровнях сопровождает текстовое описание, где кратко указано назначение того или иного элемента и его взаимодействия. Если элемент взаимодействует с рядом внешних сервисов, их стоит указать в тексте, чтобы не “мусорить” в диаграмме. Это же правило касается и интегрируемых административных панелей, число которых может доходит до нескольких десятков.
Как использовать
Если вы пользуетесь внутренней вики на базе Confluence (как мы), диаграммы легко строить с помощью плагина PlantUml, достаточно вставить референс
!includehttps://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
в код диаграммы, чтобы пользоваться элементами C4. Можно также использовать всем известный сервис draw.io, где диаграммы строятся не кодом, а блоками.
Такой вариант не кажется оптимальным, поскольку изменение даже одной диаграммы контейнеров может превратиться в Сизифов труд.
Обновление
Что
Одна из (если не главная) боль подобной документации - поддержание ее актуального состояния. Плюсом именно HLD-документации является ее не столь стремительные изменения: в рамках верхнего уровня (контекста) ситуация меняется довольно редко, уровень контейнеров более подвижен, но и там можно справиться с потоком обновлений, если четко определить, что считать изменением.
Поскольку за основную единицу уровня контейнеров был взят микросервис, изменениями решено считать:
Введение/выведение из эксплуатации микросервисов;
Интеграция сервисов с новыми внешними провайдерами;
Изменение зависимостей между микросервисами;
Переход на новые административные панели в микросервисах.
Когда
На этапе квартального планирования каждая фича проходит архитектурное ревью, в рамках которого команда архитекторов проверяет техническое решение (подробнее о процессе стоит рассказать отдельно) и помечают фичу флагом ArchIsChanged в таск-трекере (мы используем Target Process). Когда фичи проверены и собраны в список, мы вместе с архитектурной командой возвращаемся к нему в конце квартала и смотрим, что удалось сделать, и что именно поменялось в итоге. Список реализованных фич за предыдущий квартал проходит дополнительную проверку на случай, если где-то флаг забыли поставить или по итогам разработки той или иной фичи были выявлены изменения, которые не упоминались в изначальном тех.решении - we are human after all.
Выводы и планы на будущее
Деление системы на основные домены и их схематичное представление помогло увидеть продукт под разными углами и найти точки роста, а для новых сотрудников - разобраться, с чем же предстоит работать. Также подобная документация теперь является отправной точкой в создании более подробных описаний компонентов для юридического отдела и регулятивных органов.
Задача оказалась объемной и заняла около квартала и ресурсы одного технического писателя. Не на все вопросы находились однозначные ответы, порой случались дебаты по количеству и составу доменов, но куда же без этого.
В планах расширить HLD-документацию и добавить направление Infrastructure, а также понять, как лучше подойти к описанию LLD-документации.