Пару лет назад я работал в SaaS-компании, которая страдала от всех возможных проблем, связанных с разработкой программного обеспечения. Код был настолько сложным, что внесение простых изменений занимало месяцы. Все проектные задачи и границы проекта оценивались только руководителем. Разработчики не понимали, какую проблему они решают. А когда не знаешь, чего ждет заказчик, многое из того, что делаешь, оказывается бесполезным. Команда не знала, что предложить заказчику.
Несмотря на то, что у нас были микросервисы, внесение одного изменения часто требовало изменений в большинстве сервисов. Архитектура была настолько сильно связана, что мы не могли разворачивать эти «микросервисы» независимо. Бизнес не понимал, почему добавление «одной кнопки» может занимать два месяца. В итоге заказчики потеряли доверие к команде разработчиков. Все были очень разочарованы, но ситуация не была безнадежной.
К счастью, я был немного знаком с Domain-Driven Design (DDD). В то время я был далёк от того, чтобы считать себя экспертом в этой области. Однако моих знаний было достаточно, чтобы помочь компании минимизировать и даже устранить большую часть из вышеупомянутых проблем.
Время идет, но подобные проблемы не исчезают в компаниях, несмотря на то, что решения этих проблем существуют и не являются каким-либо секретом. Возможно, люди просто не знают об этих решениях. Может быть, старые подходы, такие, как GRASP (1997), SOLID (2000) или DDD (2003), забылись или считаются устаревшими? Это напоминает мне ситуацию, которая происходила в тёмные века, когда древние знания попросту были утеряны. То же самое происходит и с этими старыми идеями: они по-прежнему актуальны и могут помочь в решении текущих проблем, но их просто игнорируют. Как будто мы живем в Темные века разработки.
Ещё одно сходство с темными веками – фокусирование на ложных идеях. В Темные века религия подавляла науку. В Темные века разработки больше внимания уделяется инфраструктуре, а не правильным подходам. Я не утверждаю, что религия не важна. Духовная жизнь очень важна, но не в том случае, когда вы страдаете от голода и болезней. То же самое относится и к инфраструктуре. Наличие потрясающего кластера Kubernetes и микросервисов не поможет вам в случае, если у вас отстойный дизайн архитектуры.
Темные века разработки ПО как системная проблема
Темные века разработки ПО – самосохраняющаяся система. А вы не можете решить системную проблему, не понимая общей картины. Системное мышление – как раз тот метод, который помогает анализировать такие сложные задачи. Я использовал эту технику для визуализации Темных веков разработки.
Вы можете ясно увидеть этот замкнутый круг. Если ничего не менять, проблем становится всё больше и больше. Как DDD может исправить это?
В отличие от большинства известных гуру программирования мы не хотим, чтобы вы просто верили в нашу историю. К счастью, мы можем научно объяснить, почему это работает. Точнее, с помощью отличной книги «Ускоряйся! Наука DevOps: Как создавать и масштабировать высокопроизводительные цифровые организации», которая основана на научных исследованиях. В исследовании, описанном в книге, упоминаются характеристики лучших и худших команд. Одним из наиболее важных факторов успешной работы является слабосвязанная (loosely coupled) архитектура.
Если вы думаете, что микросервисы могут гарантировать вам слабосвязанную архитектуру, – вы сильно ошибаетесь. Я несколько раз видел микросервисы, которые были связаны сильнее, чем монолит. Вот почему нам нужно нечто большее, чем просто инфраструктурные решения. Это время для Domain-Driven Design.
Из книги «Ускоряйся! Наука DevOps: Как создавать и масштабировать высокопроизводительные цифровые организации»:
Это ключевое свойство архитектуры позволяет командам легко тестировать и разворачивать отдельные компоненты или службы, даже когда организация растет и число систем, которыми она управляет, увеличивается. То есть это свойство позволяет организациям повышать производительность по мере их масштабирования.
(...) использование новейшей архитектуры микросервисов, развернутой в контейнерах, не гарантирует более высокой производительности, если вы игнорируете эти характеристики.
(...) Архитектурные подходы, позволяющие использовать эту стратегию, включают использование ограниченных контекстов и API-интерфейсов как способ разделения больших доменов на более мелкие, менее связанные единицы, а также использование двойников в тестах и виртуализации для изолированного тестирования служб или компонентов. Ускоряйся!
DDD не работает
Может быть, вы знаете кого-то, кто пробовал DDD, и у них не взлетело?
Может быть, вы работали с человеком, который плохо разбирался в DDD, пытался внедрять эти техники и сделал всё слишком сложным?
Может быть, вы видели в Твиттере, что какой-то известный разработчик сказал, что DDD не работает?
Может, вы думаете, что это просто легендарный Святой Грааль, который, как кто-то утверждает, работает, но никто этого не видел?
Давайте не будем забывать, что мы живем в Темные века разработки программного обеспечения. Когда речь идет об идеях предыдущей эпохи, нужно понимать, что не все были очевидцами появления DDD. Это неудивительно, учитывая, что DDD появился в 2003 году.
Нелегко войти в мир DDD. Во многих книгах и статьях упускаются или чрезмерно упрощаются наиболее важные моменты DDD. Их часто объясняют на абстрактных примерах, оторванных от реальности. Также нередко можно увидеть слишком длинные и слишком сложные примеры, которые невозможно понять.
Позвольте мне попытаться объяснить DDD самым простым способом.
От Темных веков к эпохе Возрождения
DDD-техники можно разделить на два вида: тактические и стратегические паттерны. Тактические паттерны подсказывают нам, как реализовать решение на уровне кода. Тактический DDD – это не rocket sience, а набор старых добрых методов объектно-ориентированного программирования. Но мало знать как, сперва необходимо понять, что будем реализовывать. И тут в игру вступают стратегические шаблоны DDD.
В большинстве источников о DDD в основном разбираются тактические паттерны. Иногда там вовсе пропускают стратегические паттерны. Но! Вы можете практиковать DDD, используя только стратегические шаблоны. В некоторых проектах использование тактических шаблонов DDD является даже излишним. К сожалению, большинство разработчиков делают совершенно иначе. Они используют только тактические паттерны без стратегических. Это ужасно.
Если кто-нибудь меня спросит, существует ли какая-либо серебряная пуля в продуктовой разработке, у меня будет только один кандидат: стратегические паттерны. Стратегический DDD помогает нам найти ответы на вопросы:
какую проблему вы решаете?
будет ли ваше решение соответствовать ожиданиям стейкхолдеров и пользователей?
насколько сложен проект?
какие фичи не являются необходимыми?
как разделить сервисы для поддержки быстрого развития в долгосрочной перспективе?
Эти вопросы необходимы как при реализации нового проекта, так и при добавлении новой функциональности или рефакторинге. Стратегические паттерны DDD дают нам возможность целостно и предсказуемо отвечать на эти вопросы.
Некоторые инженеры говорят мне, что они «просто инженеры». Они не слишком заботятся о том, кто использует их программное обеспечение и зачем. Они просто делают, скажем, таски из JIRA: создают сервисы с некоторыми байтами на входе и некоторыми байтами на выходе. Такой образ мышления приводит к большому разрыву между инженерами и их клиентами, проблемы которых мы как инженеры пытаемся решить. Без достаточного общения нелегко создать решение, которое поможет людям правильным образом. А ведь целью является именно это, а не просто обработка байтов.
Также заманчиво сэкономить время на этапе планирования проекта, чтобы начать писать код как можно скорее и закончить раньше. Моя любимая фраза на этот случай: «пять дней кодинга могут сэкономить один день планирования». Незаданные вопросы не растворятся волшебным образом.
Лучший способ преодолеть Темные века разработки – атаковать их с разных сторон. Давайте посмотрим, как паттерны DDD могут атаковать эту систему.
Event Storming
Event Storming кардинальным образом меняет подход к стратегическим паттернам и разработке ПО в целом. Я не могу понять, почему эту технику ещё не используют во всех командах в мире.
Event Storming – это воркшоп, во время которого люди, у которых есть вопросы, (обычно это разработчики) встречаются с людьми, у которых есть ответы (обычно стейкхолдеры). Во время сессии они вместе могут быстро разобраться со сложными бизнес-доменами. В начале вы исследуете доменные события (клеите оранжевые стикеры). Event Storming настолько гибок, что с его помощью вы можете проверить, соответствует ли ваше решение ожидаемым требованиям. Вы также можете поставить другую цель сессии, например, изучить потоки данных, потенциальные проблемы или UX.
Проверка того, что решение не содержит пробелов и соответствует запросам пользователей, занимает несколько минут. Внесение изменений и проверка идей в разработанном и развернутом коде обходятся значительно дороже. А замена стикера – что может быть проще?
Инженер-строитель или инженер по ракетостроению могут быстро увидеть результат ошибки. Они могут заметить, что что-то явно не так, еще до завершения строительства. С программным обеспечением это не так просто, потому что проблему нелегко увидеть. Большинство наших важных решений не повредят никому здесь и сейчас. Проблемы с разработкой фич и более сложным обслуживанием не появятся за один день.
Event Storming подходит, когда вы планируете и большой проект, и просто одну задачу. Вопрос только в том, сколько времени вы хотите потратить. Когда мы используем его для одной истории, это может занять от 10 минут до нескольких часов. На сессии по большим проектам мы, как правило, тратим от одного дня до нескольких дней.
После сессии у вас должен быть правильный ответ на эти вопросы:
какие проблемы вы пытаетесь решить? – вместо того, чтобы гадать, что может быть полезно для конечного пользователя, или предполагать, что «мы же лучше знаем»;
довольны ли стейкхолдеры предлагаемым решением? – а не проверять это спустя полгода после внедрения;
очевидна ли сложность проблемы? – понятно ли, почему добавление одной кнопки может потребовать тонны работы;
верна ли первоначальная идея о том, как сгруппировать микросервисы по функциям, – вместо того, чтобы слепо группировать «похожие вещи».
В конце концов вы получите гораздо больше доверия стейкхолдеров, потому что вы вместе планируете решение. Это гораздо лучший подход, чем изолированное написание кода в подвале.
Что ещё примечательно в Event Storming, так это то, что результат правильно выполненной сессии может быть легко сконвертирован в код. Это должно помочь вам избежать многих дискуссий во время разработки и значительно ускорить вашу работу.
Вы должны начать с четкой цели. Время может пролететь незаметно, и не успеешь оглянуться, как обнаружишь, что ты потратил полгода на проект, который никому не нужен. Был у вас такой опыт? Это случается чаще, чем вы думаете, вот почему некоторые люди теряют доверие к «инженерам». Вот так мы и становимся разработчиками без какой-либо свободы действий.
Часто люди беспокоятся о том, сколько времени нужно «потерять» на планирование. Это неправильный подход. Вместо этого подумайте о времени, которое вы потеряете, если не проведете воркшоп. Я слышал историю, когда проведение одной сессии Event Storming остановило реализацию проекта, которая уже пару месяцев шла неправильно. Во время сессии члены команды обнаружили, что предположения, на которых они строили свою работу, совершенно неверны. Продолжение проекта привело бы к полному провалу. Даже если сессия сначала казалась пустой тратой времени, потом выяснилось, что компания избежала пары месяцев бесполезной разработки.
Event Modeling
В 2018 году Адам Димитрук предложил методику Event Modeling. Суть метода и идея в значительной степени основаны на Event Storming, но добавляют несколько новых функций, а также делается дополнительный акцент на UX-части сессии.
В общем, эти методы довольно совместимы. Даже если вы уже используете Event Storming, вы можете найти некоторые ценные подходы и в Event Modelling.
Вы можете прочитать больше об этой технике на eventmodeling.org.
Ограниченный контекст и границы транзакций (агрегаты)
Ограниченный контекст – это еще один стратегический паттерн DDD, который помогает нам разделить большие модели на более мелкие логические части.
Это ключ к достижению надлежащего разделения сервисов. Если вам нужно переделать половину системы, чтобы реализовать и протестировать новую фичу, – у вас неправильное разделение.
Альтернативой неправильному разделению является отсутствие разделения. Часто симптомом отсутствия разделения являются Божественные объекты (огромные объекты, которые слишком много знают или слишком много делают). В случае, если изменения касаются единственного сервиса, повышается риск крупных системных сбоев, а изменения становятся сложнее.
Другими словами, развивать ваш проект будет сложнее.
Отличным инструментом, который помогает в поиске Ограниченных контекстов и агрегатов, является (конечно же) Event Storming.
После воркшопа вы можете увидеть, как должны быть распараллелены ваши сервисы и какие у них есть точки соприкосновения.
Единый Язык
Единый язык – это стратегический паттерн DDD, который отвечает за создание общего языка между разработчиками, службой эксплуатации, стейкхолдерами и пользователями. Это наиболее недооцененный паттерн DDD. Потому что кого волнует язык, верно?
Мне потребовалось время, чтобы понять, сколько проблем в общении между разработчиками и не-разработчиками возникает из-за использования различных языков. И как это больно. Я советую обратить внимание на это. Из-за недопонимания разработчики решают не ту проблему, поскольку никто не понимает, чего от них ожидают.
Вы бы удивились, если бы я сказал вам, что Event Storming поможет вам вырабатывать Единый язык? Делая воркшоп совместно со стейкхолдерами, вы провоцируете общение. Трудно найти решение, когда вы не можете понять друг друга. Вот почему очень важно не забывать звать стейкхолдеров на воркшоп!
Решает ли DDD все проблемы?
Даже если DDD великолепен, он не решает всех проблем, которые у нас есть. Умерьте свои ожидания. Даже если мы виртуозно используем эти методы в моей команде, у нас все еще есть сомнения, достаточно ли хорош созданный дизайн. Иногда мы не знаем, как подойти к проблеме. Иногда мы возвращаемся от кода к фазе проектирования. Иногда мы принимаем плохие решения. Всё это случается, но в мире нет команды без этих проблем. Лучше сразу допустить, что это может произойти, и не удивляться. Но мы знаем, что без DDD эти проблемы были бы гораздо значимее.
При использовании DDD следует уделять особое внимание тому, чтобы избежать:
Big Design Up Front,
написания кода «на будущее»,
попытки создать что-то совершенное.
Вместо этого:
Сосредоточиться на поставке MVP пользователю за короткое время (под коротким я подразумеваю скорее 1 месяц, чем 6 месяцев).
Если вам нужно реализовать что-то «на будущее», потому что позже будет сложнее добавить это, – это очень плохой знак. Вам следует подумать о том, как упростить позднее добавление.
Примите тот факт, что даже если вы сделаете всё возможное, ваш дизайн не будет идеальным с самого начала – гораздо продуктивнее улучшать его в процессе использования.
Некоторым командам необходимо многое поменять, чтобы перейти на этот уровень. Но мой опыт говорит, что это возможно. И удовольствие, которое вы получите от работы, того стоит!
Если вы считаете, что должны быть техлидом, чтобы предлагать такие улучшения, – вы ошибаетесь! По началу, когда я не был техлидом, я уже предлагал много улучшений в командах, в которых я работал. Вам нужны хорошие аргументы для ваших коллег по команде. Мы всегда объясняем в наших статьях, почему эти методы работают. Наших аргументов должно быть достаточно, чтобы убедить их. Если это не сработает из-за того, что ваша команда близорука, это хорошая причина подумать о смене работы.
Возрождение программного обеспечения
Трудно досконально разобраться с представленными техниками в рамках одной статьи. Моя цель состояла скорее в том, чтобы вдохновить вас подвергнуть сомнению статус-кво. Не так должна работать наша индустрия. Если вас не устраивает статус-кво, я надеюсь, что вдохновил вас на изучение новых техник. Это лучший способ борьбы с Темными веками разработки.
Если вы хотите узнать о тактических паттернах DDD, ознакомьтесь с нашими предыдущими статьями.
Примеры написаны на языке Go, но их можно легко перенести на любой язык. Go имеет очень низкую точку входа. Кто знает, может быть, вы найдете новый любимый язык? ?
А как насчет стратегических паттернов DDD? Эта статья на самом деле является введением к серии статей. В последующих публикациях мы рассмотрим наиболее важные стратегические паттерны DDD на полностью рабочем проекте.
Кто вы в Темные века разработки?
Обычный невежа, который слепо следует правилам, навязанным другими?
Инквизиция, которая будет ненавидеть и пытаться подавлять любой необычный подход?
Алхимик, пытающийся создать золото? Даже если это не подкреплено с научной точки зрения?
Или, может быть, вы тайно читаете запрещенные книги в своем подвале? Может быть, вы хотите вместе с нами покончить с Темными веками и начать эпоху Возрождения разработки программного обеспечения?
Дайте нам знать в комментариях!
От переводчика: или присоединяйтесь к нашему сообществу)