Как спроектировать продукт, чтобы не зарыть деньги в землю
На каком этапе создания продукта или системы подключать архитектурное проектирование системы, чтобы потом не было мучительно больно за потраченные деньги? Как решить, совмещать ли CQRS и микросервисы.
Эта статья для представителей бизнеса с запросом на разработку ИТ-решения. Мы подскажем, как запустить продукт и избежать неоправданных затрат, связанных с архитектурой. А также посмотрим, как использование CQRS поможет при реализации функционала в разных клиентах приложения, и являются ли микросервисы той самой панацеей.
Коротко о CQRS
CQRS (Command-Query Responsibility Segregation) — шаблон, применяемый при разработке систем, который гласит, что любой метод системы может быть либо запросом (не изменяющим состояние системы), либо командой (изменяющим состояние системы). Как показывает практика, это один из самых часто применяемых шаблонов при разработке ПО. Его можно применять на разных уровнях и в различных ситуациях. Например, классическое разделение систем на OLTP/OLAP, когда данные пишутся часто в OLTP-систему, а читаются из OLAP-системы, является ни чем иным как применением шаблона CQRS в архитектуре БД.
В “древние” времена (начало 2000 годов) популярные системы подталкивали к применению CQRS. Например, при использовании Interbase/FirebirdSQL рекомендуется использовать разные типы транзакций для чтения и записи. В современном мире очень часто встречается случай сосуществования двух систем на разных уровнях архитектуры. Например, может быть разделение на уровне различных систем, когда личный кабинет клиента на сайте реализует только Query-функциональность, а все изменения происходят в CRM-системе внутри компании через заранее определенные интерфейсы Command. Можно найти примеры использования CQRS на уровне архитектуры JS приложения. Кто бы мог подумать несколько лет назад, что слова архитектура и JS будут использоваться в одном приложении… Хотя, возможно, это излишний стеб.
Две крайности при разработке
Типичная ситуация: A Big Ball of Mud
Очень часто встречается эволюционный подход к разработке. Как правило, сначала разрабатывается MVP без проработки архитектуры и анализа нефункциональных требований, затем начинается долгий этап эволюционных доработок. В итоге мы получаем плохо функционирующую систему, которую трудно дорабатывать. В критических случаях — еще и написанную с использованием языка программирования, для которого очень трудно найти разработчиков. Примечательно, что к таким последствиям можно прийти вне зависимости от опытности команды и качества менеджмента. Все стараются делать лучшее в имеющихся на тот момент условиях, но с течением времени получается результат далекий от идеала.
По нашему опыту в случае отсутствия регулярной практики по архитектурному проектированию, неизбежно наступает момент когда систему приходится переписывать целиком. На это тратится больше сил и средств, чем на всю предыдущую разработку. И самое страшное, что приходится переучивать всех пользователей для работы с новой системой. В некоторых случаях это сопоставимо по стоимости с разработкой самой системы.
В таких ситуациях от основателей и стейкхолдеров бизнеса можно услышать фразы: “Нам бы побыстрее выйти в релиз и получить первых клиентов. А потом уже давайте думать, что делать с клиентами и как улучшать наш продукт”. При таком подходе, когда случается реальный наплыв первых клиентов, система начинает валиться, и вся команда начинает тушить пожары. В такой ситуации иллюзия “хорошей жизни” внезапно развеивается. Разработчики и все держатели контекста постепенно покидают команду. Клиенты недовольны. В результате бизнес рушится из-за того, что не смог масштабироваться.
Микросервисы не спасут
В последнее время микросервисы очень популярны. При этом некоторые заказчики на волне хайпа требуют, чтобы все было реализовано с использованием микросервисной архитектуры, не зная реальную стоимость ее применения. Чтобы получить пользу, надо уметь правильно микросервисы готовить. Не достаточно просто сделать несколько независимых проектов и назвать это микросервисами.
При разделении системы на микросервисы возникает много вопросов по взаимодействию систем. Например, как отследить цепочку вызовов различных сервисов, которая привела к конкретной ошибке. Или как понять, какой сервис сейчас является узким местом в работе системы. Эти вопросы с разной степенью успешности решаются либо готовыми инфраструктурными решениями (Elastic для логгирования), либо под них надо разрабатывать какие-то свои инфраструктурные сервисы. К примеру, балансировщик, который учитывает особенности бизнес-логики при маршрутизации запросов. Эти проблемы характерны не только для микросервисных систем, но и для всех распределенных систем.
В большинстве случаев, крупные вложения в разработку инфраструктуры, особенно на начальном этапе создания системы, не оправданы. Не известно, будет ли система работать сколько-нибудь продолжительное время, или бизнес-идея не оправдает себя, и проект будет закрыт. Мы не знаем, сколько пользователей к нам придет. Известны случаи, когда в большом проекте стоимость развертывания всей инфраструктуры для нескольких сервисов в нескольких конфигурациях, проверка работоспособности, масштабируемости и т.д. превышала 1 млн рублей. В среднем проекте реализация бизнес-логики стоит ощутимо меньше. Все же это расходы, которые на данном этапе нецелесообразны.
Кроме того, грамотно разделить систему на независимые микросервисы на начальном этапе, как правило, не получается по причине того, что еще не известен ни точный функционал проектируемой системы, ни структура предметной области. Следовательно, неправильно выбранное разделение по сервисам приводит к сложностям и неминуемо к дополнительным расходам при реализации необходимых сценариев работы системы. В некоторых случаях — и к полной невозможности корректного функционирования системы. Система, построенная на микросервисах, является частным случаем распределенной системы и подвержена действию CAP-теоремы. И если заранее не заложить механизмы обеспечения целостности данных, про что сейчас очень часто забывают, то в реальной эксплуатации можно получить очень много неприятных сюрпризов в виде потери или рассинхронизации данных.
Золотая середина
Продуманная архитектура — залог успеха
При разработке традиционных монолитных систем возникают те же самые вопросы по грамотному описанию/разделению предметной области. Неправильное разделение системы на слабо связанные контексты приведет к нежелательным последствиям: к очень сложному внесению изменений, невозможности отследить и проверить все сценарии поведения. Ошибки будут возникать и нарастать как снежный ком в самых неожиданных местах.
Останется ли заказчик/пользователь доволен работой системы зависит от того, насколько грамотно она спроектирована. Поэтому крайне важно задуматься о проектировании системы как можно раньше. Причем, для этого, необходимо понять работу системы на всех уровнях, кто и как будет пользоваться системой.
Пример
Скажем, мы делаем интернет-магазин, но заказчик совершенно забыл про то, как работает доставка и что кроме того, чтобы продать необходимо еще и укомплектовать заказ, и для этого надо сделать удобный интерфейс комплектовщика или сделать интеграцию разрабатываемой системы с системой складской логистики. Говоря простым языком, если мы не представляем себе хотя бы основные бизнес-процессы, в которых будет принимать участие наша система (т.е. бизнес-архитектуру), то мы можем упустить очень большую часть потребностей и оказаться в ситуации, когда все вроде сделали правильно, но пользоваться системой нельзя и надо где-то искать дополнительные средства на доработку.
Не редкость в таких ситуациях отсутствие средств, поскольку оставшиеся к этому моменту бюджетные ресурсы были потрачены на дополнительные, не столь критичные функции. Не проработав до конца все взаимодействия разрабатываемой системы с другими системами (т.е. архитектуру решения), мы можем начать делать дорогие и бессмысленные на данный момент вещи, например разрабатывать свою CRM-систему в то время, когда заказчику не надо никакой особенной функциональности и можно использовать готовое решение. И только проработав и поняв окружение разрабатываемой системы, можно обоснованно принимать решение о выборе программной архитектуры, разделения системы на слабосвязанные контексты и т.д.
Так с чего начать?
На практике для выработки таких архитектурных решений хорошо помогает одно- или двухдневный воркшоп с заказчиком, на котором прорабатывается не только архитектура решения, но и low-fidelity дизайн системы и основные сценарии использования. В случае со стартапами чрезвычайно важным шагом является проработка Business Canvas вместе с заказчиком (если его еще нет) для того, чтобы все стороны поняли жизнеспособность идеи. Не исключено, что результатом будет закрытие проекта сразу после воркшопа: он поможет стейкхолдерам увидеть несостоятельность бизнес-замысла, не потратив время и деньги на техническую реализацию. Как бы странно не звучало, даже в таких ситуация очень сильно возрастает доверие между участниками проекта. Одним из результатов воркшопа будет являться документ, описывающий нефункциональные требования к разрабатываемой системе.
Необходимо отметить, что даже проведенный воркшоп не гарантирует правильности и полноты полученных данных. С течением времени бизнес-идея может измениться из-за внешних обстоятельств. К примеру, сейчас происходят очень большие изменения в структуре работы практически любого бизнеса. Мы точно знаем, что мир через пару месяцев будет уже не такой, как раньше (привет, COVID-19 и карантин!). В этом случае при разработке системы можно использовать хорошие практики в виде шаблона CQRS на уровне архитектуры приложения и с большой долей вероятности это позволит заново использовать написанную функциональность за счет разделения на независимые компоненты.
Что сделали мы
В одном из проектов по управлению и автоматизации бизнес-процессов компании с флотом морских судов перед нами стояла задача в минимальные сроки и с минимальным затратами выпустить первую версию ПО. Был выбран подход внедрения CQRS на уровне логической архитектуры. Базовая архитектура приложения была выстроена с соблюдением разных принципов и шаблонов проектирования: MVVM, SOLID, CQRS и т.д. Это позволило переиспользовать функциональность фич для разных клиентов приложения. При этом, внедрение не занимало много времени и было достаточно недорогим. Реализация данного подхода не потребовала дополнительных затрат, так как при старте разработки у команды были уже наработанные классы и один уровень понимания архитектуры приложения. В последующих доработках этот подход полностью себя оправдал: большой процент функциональности можно было просто гибко переиспользовать. При таком подходе заказчик существенно сократил затраты на реализацию функций, определённые части которых дублируются для разных клиентов приложения.
Напоследок
Ошибочно Agile-подход к разработке трактуется как “не надо ничего проектировать заранее, надо ввязаться в бой, а там война — план покажет”. А если нам будет не хватать скорости работы или скорости изменения программы, мы зарядим серебряную пулю — микросервисы. Ведь на всех конференциях рассказывают, что микросервисы — это просто и решает сразу все проблемы. Такой оптимизм, как правило, ведет к потере денег и неработающему продукту.
С другой стороны, проектировать все заранее в нашем быстро меняющемся мире практически невозможно. Как всегда надо находить баланс. Во-первых, проработка архитектуры решения позволяет осознанно выбрать подходящее решение на текущем этапе и в итоге сэкономить денег заказчику. Во-вторых, документирование принятых архитектурных решений позволит в дальнейшем понять, почему именно такие решения были приняты. В-третьих, периодическая валидация изменившихся условий позволяет снизить риски отказов в работе решения и принять взвешенное решение о необходимости доработки или изменения. Таким образом, следование грамотным практикам непосредственно при написании кода позволяет получить хороший фундамент, на основании которого можно будет масштабировать решение в дальнейшем.
DmitriyTitov
Заголовок у статьи традиционный, полусектантский, про микросервисы. Ладно.
Введение у статьи интересное, разумное и обещающее полезный материал.
Содержание у статьи, хм, отсутствует.
О чём статья, может я не разглядел?
MrAwesome
Используйте MVVM, SOLID, CQRS и тогда все будет хорошо, но это не точно.
koniglabs Автор
Спасибо за комментарий. Обновим и добавим конкретики про использование.
koniglabs Автор
В данной статье просто не хотелось уходить в детали решения того как базово применять данные подходы. Таких статей на мой взгляд много. Хотелось сделать именно акцент на том что применять проектирование даже и особенно на этапе MVP нужно! Что нужно с точки зрения бизнеса не забывать об архитектуре даже в те моменты, когда хочется быстро «накалбасить» решение и зарелизить что бы проверить гипотезы.
Но прочитав комментарии соглашусь что надо добавить больше тех. информации с обоснованиями в содержание.
DmitriyTitov
Ну вот не факт, с моей точки зрения. Есть вообще всякие термины типа throw-away prototype. И иногда, полагаю, действительно можно сделать первую версию абы как. Зависит от ситуации.
А вот сразу продумывать архитектуру может означать удариться в микросервисы, или даже, прости господь, в блокчейн. За всё это платить придётся сразу, а ценности продукта в итоге может и не получиться.
koniglabs Автор
Вот тут уже интересно=). Хочу задать пару вопросов:
1)Почему для вас фраза «продумывать архитектуру» ассоциируется в первую очередь с блокчейном и микросервисами а не с бизнесом к примеру? =) Какую для вас ценность несёт термин «достаточная» проработка архитектуры?
2)Да, прототипирование, в том числе и rapid prototyping существуют и имеют вполне себе осмысленное право на существование. При этом, хочу обратить ваше внимание что так же существует Эволюционное прототипирование. Мы к примеру так же использовали на мой взгляд удачно быстрое прототипирование в кейсе https://koniglabs.ru/case_study/1-2stundeuben%e2%80%8c/. Но при этом я не скажу что это Minimum Viable Product. Может вопрос в целях использования прототипирования? =)
Как пример: Можно сделать UX прототипирование достаточно быстро и без строчки кода. При этом цель тут понятная- изучение пользовательского опыта. Но глупо ставить к примеру целью UX прототипирования: Понять концепцию решения сложной математической задачи.