Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Один из самых естественных треков развития для программиста – стремиться стать программным архитектором. Проектирование систем – непростое дело, а для того, чтобы построить сложную систему, нужно как быть экспертом-технарём, так и обладать опытом в предметной области. На наработку этих навыков требуется немало времени и усилий. Читая книгу Марка Ричардса и Нила Форда «Фундаментальный подход к программной архитектуре», я нашёл у них метафору «архитектурных ката». В их трактовке ката – это отработка решения для элементарной задачи. Я решил опробовать подход с ката, чтобы прокачать мои навыки проектирования систем.
Примечание: ката (kata) – это японские боевые и гимнастические тренировки. Этот термин в IT вводит Марк Ричардс в своей книге «Фундаментальный подход к программной архитектуре», объяснено на с. 99-100.
Дорожная карта
В этой статье рассмотрены:
- Базовый подход к постановке задачи;
- Мой подход к выработке решения;
- Моё решение задачи.
❯ Постановка задачи
Поставлена задача спроектировать по заказу компании платформу для издания книг. На этой платформе должны быть предусмотрены следующие возможности:
- Посетители должны иметь возможность искать и покупать книги;
- Авторы должны иметь возможность загружать свои книги для предварительного просмотра;
- Сотрудники компании должны знакомиться с книгами, управлять ими и информировать клиентов о них.
Подробный текст ката с описанием конкретных требований приведён здесь.
«Дополнительный контекст» ката – это важный блок, на основе которого можно вывести дополнительные ограничения и предположить, чего логично ожидать от проектируемой системы. Давайте разберём всё по порядку:
Бизнес пришёл к данному решению, поскольку у конкурентов есть схожее предложение
Это может означать, что на нашей платформе должны быть реализованы, как минимум, те же базовые возможности, и выдержан тот базовый уровень качества, при котором платформа казалась бы привлекательной как авторам, так и читателям. Большинство возможностей на ней должно предоставляться с самого начала (в другом варианте некоторые возможности допустимо добавлять поступательно, но тоже очень быстро), поскольку, если первичное предложение кажется неудовлетворительным, то компания просто не сможет набрать пользовательскую базу. Также это может значить, что платформа должна быть интегрирована с уже существующими, чтобы миграция на вашу платформу не составляла труда ни для авторов, ни для читателей.
Идёт жёсткая конкуренция за авторов
Это означает, что все возможности, ориентированные на авторов, должны быть высокого качества, а сам сервис должен быть логично выстроен, долговечен и круглосуточно доступен.
Это часть долгосрочной стратегии по модернизации издательской составляющей большого бизнеса
Из этого пункта можно сделать вывод, что система должна быть рассчитана на масштабирование и расширяемость, но исходное решение должно интегрироваться с уже имеющимися неновыми технологиями или бизнес-моделью, отдельные процессы в которой явно пробуксовывают.
Информация, необходимая для публикации книги (дистрибуция, авторские отчисления, маркетинг) поступает из нескольких разрозненных систем
Опять же, этот аспект связан с расширяемостью и интеграцию. В идеале рано или поздно все источники должны быть спрятаны под одним красивым пользовательским интерфейсом, а ненужные – вообще устранены.
❯ Как подойти к задаче
Существует множество инструментов, помогающих начерно спроектировать систему и очертить её контуры. Так, C4 model помогает визуализировать систему. Работа с этим инструментом предполагает нисходящий подход: сначала делается максимально высокоуровневый обзор, после которого путём постепенной декомпозиции компонентов мы спускаемся на уровень классов (объектно-ориентированное проектирование). Согласно концепции предметно-ориентированного проектирования, систему нужно подразделять по линиям разграничения её внутренних стыкующихся областей, тем самым обеспечивая их изоляцию и приспосабливаемость системы. Я считаю, что, приступая к решению такой задачи, важно иметь в виду пользователя.
Поэтому обычно я придерживаюсь такого подхода:
- Выявляю основные категории пользователей и их роли;
- Перечисляю функциональные требования (на основе пользовательских историй, юзкейсов или другой подобной информации);
- Очерчиваю ключевые предметные области (в соответствии с DDD);
- Проектирую ключевые компоненты в соответствии с функциональными требованиями в пределах предметных областей и с захватом нескольких предметных областей сразу, соотношу те нефункциональные требования, которые есть у этих компонентов;
- Выбираю технологии для реализации.
Итак, давайте попрактикуем ката, ориентируясь на перечисленные выше соображения!
❯ Основные пользователи
Из постановки задачи прямо следует, что на платформе должны поддерживаться, как минимум, три пользовательские роли:
- клиенты
- авторы
- редакторы/рецензенты
Но из описания также понятно, что у нас ещё будут:
- инженеры технической поддержки (которые будут помогать пользователям решать возникающие проблемы)
- менеджеры по заказам и выставлению счетов (их задача – отслеживать продажи книг и управлять самими продажами, доставкой и платежами)
Функциональные требования
Даже сейчас уже хорошо просматриваются несколько предметных областей, на которые будет разделена система. Ради ясности изложения, давайте разделим по этим же областям пользовательские истории. Я подберу несколько тривиальных названий для пользовательских историй и несколько гипотетических, которые кажутся мне важными и дополняют общую картину.
Каталог:
- Пользователь может просматривать список книг;
- Пользователь может просматривать подробности об отдельно взятой книге;
- Пользователь может листать список книг;
- Пользователь может искать книги.
Оформление заказов:
- Пользователь может добавлять книги в корзину или удалять их оттуда;
- Пользователь может просматривать корзину заказов;
- Пользователь может сделать заказ с учётом состояния его корзины;
- Пользователь может выбрать вариант исполнения книги (электронная, бумажная);
- Пользователь может выбрать адрес и время доставки, а также компанию, которая доставит ему бумажную книгу;
- Пользователь может просматривать свои заказы (подробности и состояние);
- Пользователь может отменять заказы;
- Пользователь может получать по электронной почте информацию о состоянии своих заказов
- Менеджер может просматривать список заказов и подробности о них;
- Менеджер может задавать состояние заказа;
- Менеджер может связаться с автором заказа (по телефону/электронной почте/в чате);
- Менеджер может направлять уведомления пользователю по электронной почте;
- Менеджер может искать заказы;
- Менеджер может поручать заказы курьеру (или просто передавать их в компанию, занимающуюся доставкой).
Редакторская работа:
- Менеджеры могут редактировать метаданные по книгам;
- Менеджеры могут публиковать книги в каталоге;
- Менеджеры могут устанавливать цену на книгу;
- Менеджеры могут одобрять/отклонять главы или целые книги ;
- Менеджеры могут оставлять комментарии по поводу рецензий на главы или книги;
- Менеджеры могут уведомлять пользователей о появлении новых глав или книг;
- Автор может отправить главу/книгу на рецензирование;
- Авторы могут оставлять комментарии по поводу полученной рецензии;
- Авторы могут принимать/отклонять изменения, внесённые в книгу при рецензировании;
- Авторы могут загружать главы в файловых форматах;
- Автор может загрузить в виде файла целую книгу;
- Автор может прямо на сайте редактировать содержимое книги и её метаданные;
- Авторы могут просматривать статистические данные о своих книгах;
- Авторы могут управлять платёжными настройками.
Учитывая все перечисленные требования, давайте подробнее рассмотрим предметные области, которых они касаются.
❯ Предметные области
Чтобы различать в системе предметные области, я пользуюсь акторами, сущностями и паттернами коммуникации. Если я понимаю эти пункты, то мне проще оценивать качество коммуникации, загрузку, паттерны доступа к данным, а также требования к хранилищу данных. Ниже разберём 5 предметных областей, которых, на мой взгляд, для первичного дизайна системы как раз хватит.
Каталог
- Акторы: читатели
- Сущности: описания книг, обложки, списки, рецензии, комментарии
- Функции: все вышеперечисленные юзкейсы
Нагрузка
Поскольку к каталогу обращаются все пользователи (как активные клиенты, так и случайные посетители), и таких пользователей могут быть миллионы. То есть, предполагается, что сервис будет работать под высокой нагрузкой. Из каталога пользователи будут получать информацию о книге, поэтому особенно высокая нагрузка будет связана с чтением.
Технические требования
1. Доступность.
Мы хотим, чтобы пользователи могли обращаться к каталогу когда угодно. Здесь можно пожертвовать согласованностью: обновление заголовка книги или даже добавление новой книги можно отображать в каталоге не сразу по факту этого действия (задержка даже в пару секунд определённо будет здесь допустима).
2. Масштабируемость.
Поскольку пользовательская аудитория будет расти (притом, что она уже и так огромная), система должна легко масштабироваться и подхватывать объём работы, пропорциональный количеству новых пользователей.
3. Эластичность.
Если в каталоге возникнет резкий всплеск трафика, то система должна быть в состоянии быстро с ним справиться, причём, чтобы для пользователей этот процесс был прозрачен, и они должны будут в состоянии продолжить работать с сайтом в привычном режиме.
4. Восстановление после отказов и устойчивость.
Вероятно, каталог – самая важная функциональная часть платформы, предоставляемая пользователям. Именно в каталог первым делом приходят новые посетители, именно в этой части платформы решаются её основные задачи. Если вдруг откажет система, обслуживающая каталог, то она должна очень быстро восстановиться, заново расставив по местам все полезные данные. В идеале эта система вообще не должна отказывать и быть устойчива к любым исключительным состояниям или ошибкам в работе сети/данных/оборудования.
Бизнес-требования
Никаких конкретных бизнес-требований здесь не выдвигаются, но вполне можно предположить, с какими из них нам точно придётся столкнуться:
- Персонализация контента
- Поддержка A/B тестирования
Корзина
- Акторы: читатели, менеджеры по заказам.
- Сущности: элементы в корзине, картинки, формы (адрес, учётные данные карты, идентификация пользователя).
- Функции: все юзкейсы, упоминавшиеся выше.
Нагрузка
К корзине будут обращаться те пользователи, которые в настоящее время являются активными покупателями, а также сотрудники, имеющие доступ к заказам и к данным об истории покупок. Серьёзной нагрузки вообще не ожидается, так как количество покупателей гораздо меньше общего числа посетителей. Однако, как только накопится достаточно большой объём исторических данных по заказам, анализ этих данных и операции над ними могут серьёзно усложниться. Вот почему важно разделить онлайновую и оффлайновую обработку заказов.
Технические требования
1. Согласованность.
Скорее всего, пользователи захотят, чтобы все изменения в их корзине заказов отображались мгновенно и предсказуемо, поэтому в данном случае согласованность – это наиболее приоритетный аспект.
2. Устойчивость.
Корзина заказов, точно как и каталог, является критически важным компонентом с точки зрения пользователя. Если действия пользователя то и дело оканчиваются неудачно, то пользователи уходят. Поэтому в корзине заказов все внутренние и внешние ошибки должны обрабатываться аккуратно, а мы должны делать всё возможное, чтобы не потерять пользовательских данных.
3. Восстановление после отказов.
Если сервис вдруг откажет, то нужно сразу же порождать новые страховочные экземпляры корзины и очень быстро наполнять их данными, чтобы пользователь мог продолжить работу. Здесь можно задействовать те или иные защитные сервисы, которые сообщали бы пользователю о возникшей проблемы и вывели ему наиболее свежее кэшированное состояние корзины – просто чтобы пользователь мог убедиться в сохранности всех своих данных.
4. Безопасность.
В описываемой корзине есть страница оформления заказа, а также страница для ввода данных карты и последующего платежа. Излишне говорить, что сервис должен обращаться с этими данными как следует.
Бизнес-требования
Конкретные бизнес-требования здесь также не указаны, но вот какими они могут быть:
- Сохранять состояние корзины заказов между данным визитом и следующим;
- Рекомендовать товары на странице оформления заказа.
Рабочий кабинет
В этом компоненте заключены функции, связаны с авторской работой.
- Акторы: авторы книг
- Сущности: документы, текст
- Функции: загрузка и редактирование глав, передача их на рецензирование
Нагрузка
Учитывая, что авторов довольно мало, ту работу, которой они занимаются, легко изолировать и, следовательно, распределить. Интенсивность конкурентной обработки здесь будет невелика, общая нагрузка, как ожидается, будет относительно низкой.
Технические требования
1. Согласованность и устойчивость.
Редактируя и загружая тексты, авторы, вероятно, будут рассчитывать на надёжное сохранение этой информации.
2. Масштабируемость.
Из-за количества книг и их объёма могут возникать проблемы с производительностью, но, поскольку каждый автор работает только со своими текстами, и вряд ли в его произведении будут сотни огромных глав, масштабируемость здесь не представляет особой проблемы.
Бизнес-требования
- Уведомления о том, что статус рукописи изменился после рецензии;
- Поддержка «бета»-версий глав.
Редакторская CRM-система
- Акторы: книжные редакторы и менеджеры по контенту
- Сущности: комментарии-рецензии, главы
- Функции: все вышеперечисленные юзкейсы
Нагрузка
Редакторский раздел похож на рабочий кабинет автора: с этим разделом работает небольшое количество пользователей, но объём данных сравним с данными каталога, поэтому операции поиска и фильтрации будут обрабатываться достаточно долго.
Технические требования
1. Интероперабельность.
Как сказано в ката, платформа должна поддерживать интеграцию с различными источниками данных и комбинировать данные в единый (редактируемый) «вид». Эти источники появляются и исчезают, поэтому сервис должен быть открыт для расширяемости и дальнейшей интеграции с добавлением новых источников.
2. Согласованность.
Поскольку редакторы работают с рецензиями и имеют доступ к содержимому самих книг, любые устаревшие данные внесут путаницу в процесс работы, поэтому от них следует избавляться.
Бизнес-требования
Исходя из постановки задачи, мы должны иметь возможность:
- Уведомлять авторов о том, что после рецензирования статус книги изменился;
- Уведомлять клиентов о выходе новых глав.
Система работы с хранилищем данных
- Акторы: сотрудники-«администраторы»
- Сущности: заказы, платежи, доставляемые книги
- Функции: все вышеперечисленные юзкейсы
Нагрузка
К хранилищу редко приходится обращаться напрямую, а также не приходится за один раз оперировать огромными объёмами данных, поэтому никакой серьёзной нагрузки не ожидается.
Технические требования
1. Высокая согласованность
Сервис хранилища данных – это источник истины по всем данным, которые касаются заказов и книг, поэтому целостность данных здесь имеет наивысший приоритет.
2. Безопасность.
В модуле должны поддерживаться: маскировка данных, ограничение данных и другие возможности, которые позволят защитить пользовательские данные от неправомерного доступа.
❯ Как спроектировать этот сервис
В Интернете найдётся множество схем подобной архитектуры, каждая – со своими достоинствами и недостатками. Но для этого поста воспользуюсь блок-схемой из этой статьи на Medium. Вот мой вариант:
Блок-схема одного из возможных решений данной задачи
Давайте разберём все компоненты этого изображения.
Общая схема
Из множества имеющихся вариантов я выбрал микросервисную архитектуру как ту, которая очень просто масштабируется и естественным образом обеспечивает разделение зон ответственности. Все сервисы логично выводятся из вышеперечисленных предметных областей и могут быть развёрнуты в Docker под k8s, в Nomad или в облаке Google/AWS Cloud. Речь идёт о следующих сервисах:
Каталог
В этом сервисе будет кэшироваться минимум данных обо всех книгах, которые необходимо отображать в каталоге, а также конкретные данные по каждой книге. Также тут будут кэшированы данные от рекомендательных или рейтинговых сервисов, если у нас такие будут. Рекомендую взять couchbase в качестве базы данных для эффективной обработки в оперативной памяти, масштабируемости и согласованности в конечном счёте. Данные о книгах обновляются через уведомления от Kafka (топик Books), как только книга будет обновлена в Books Service. В целом вся система спроектирована так, чтобы выдерживать высокую нагрузку при чтении.
Ещё одна важная деталь заключается в том, что по соображениям безопасности веб-трафик для этого сервиса маршрутизируется через специальный балансировщик нагрузки и проходит через подсеть (мы не хотим, чтобы клиенты обращались к тем сервисам, которые для них не предназначены).
Заказы
Этот сервис спроектирован так, чтобы в нём хранились все важные данные о заказах, сделанных клиентами. Поскольку согласованность здесь имеет наивысший приоритет, данные по этому сервису мы будем хранить в PostgreSQL. Как только заказ сформирован, подробности по нему передаются в топик Orders, хранящийся в Kafka.
Книги
Этот сервис будет отвечать за хранение содержимого книг, доступность этой информации, распространение и доставку. Когда содержимое книги меняется, все заинтересованные стороны уведомляются об этом через топик Books в Kafka, а информация о доступности записывается в топик Books-Lock.
Пользователи
Содержит важную информацию о пользователях (клиентах, авторах, сотрудниках), совместно используемую разными сервисами.
Центр уведомлений
Отвечает за отправку уведомлений разным пользователям (клиентам, авторам, сотрудникам) через целевые каналы (электронная почта, SMS, push-уведомления, т.п.).
Авторская система управления содержимым
Это клиентский API для писателей, в котором реализованы такие функции: работа с документами, прохождение процесса рецензирования, просмотр профиля и прочая логика, а также коммуникация с различными внешними сервисами. В Couchbase должны кэшироваться как данные рецензий, так и документы – до тех пор, пока глава или книга не будет одобрена редакторской группой.
Админка для сотрудников
Это клиентский API для разных сотрудников, управляющих системой: редакторов, менеджеров по контенту, инженеров службы поддержки и других.
Спасибо, что дочитали! Расскажите, как на ваш взгляд можно было бы улучшить или сломать эту систему.