Эргономичный подход v1.0M1

April 9, 2022

Предисловие

Я рад сообщить о завершении этапа прототипирования и проверки гипотезы Эргономичного подхода и переходе к стабилизации и продуктизации.

Работу над Эргономичным подходом я начал весной 2020 года. Причиной тому стал возврат к работе над стандартными для экосистемы Spring-а проектами после четырёхлетнего перерыва.

На контрасте с работой над своими проектами, я особенно остро ощутил уже забытые чувства раздражения от избыточной трудоёмкости решения задач и страха внести регрессию. Уже тогда я понимал, что причины и того и другого кроются во многих широко распространённых практиках - пакетировании по слоям, связном граф анемичных JPA-сущностей, глобальном компонент скане, тестах на моках. В своих проектах я делал это как-то по-другому, но именно "как-то" - каждый раз уникальным образом на основе интуиции.

Для того чтобы вытащить эту интуицию наружу и сначала систематизировать собственную работу, а потом научить других делать так же и систематизировать работу команды, я начал писать книгу. Всё началось с этого наброска:

ergo approach first sketch

На удивление эта диаграмма с точностью до терминов всё ещё актуальна на 100%. Но она стала лишь частью одного из трёх столпов Эргономичного подхода - декларативного стиля (два других - концептуальная декомпозиция и тестирование требований).

Главным пробелом в системе моей работы была декомпозиция кода на модули. В первом описании подхода к декомпозиции у меня классы сами разбивались на кластеры, а если нет, то "надо смотреть каждый конкретный случай и универсального рецепта у меня нет".

Поэтому главное, что я сделал за эти два года - нашёл практический ответ на вопрос "Если не на слои, то как по другому декомпозировать систему?". Я считаю именно это главной ценностью Эргономичного подхода, альтернативы которой нет ни в одном другом источнике, включая DDD, Чистую Архитектуру, работы Парнаса и ещё пяток книг по проектированию приложений.

И хотя работа ещё не окончена, я уже сейчас хочу выразить благодарность руководителям в ряде компаний:

  1. Кожевникову Максиму - Трейдинг Клаб Раша
  2. Макарову Николаю - Брума Сервис
  3. Кармакову Владу, Степанову Владимиру и Бурмину Леониду - Сибериан.про
  4. Валиеву Олегу и Тимофееву Евгению - БрендМейкер-ру

Я бы не довёл Эргономичный подход даже до текущего состояния, если бы они не поверили в мой профессионализм и не дали мне возможности делать проекты так, как я считаю правильным.

Со всеми этими компаниями я сделал больше одного проекта, представители трёх из них подписаны на мой канал, а в одной из них я отвечаю за направление разработки под JVM - на мой взгляд, всё это говорит о хороших для бизнеса результатах работы по Эргономичному подходу.

Если вы тоже хотите, чтобы ваши программы приносили больше положительных чувств всем стейкхолдерам от разработчиков до учредителей - присоединяйтесь к бета-тестированию Эргономичного подхода. Для этого напишите мне:

Я могу предложить широкий спектр вариантов сотрудничества - от разовых консультаций до реализации проектов под ключ.

Также хочу поблагодарить читателей моего канала - небольшой, но стабильный и естественный прирост количества подписчиков значит для меня, что моя работа нужна людям и это даёт мне дополнительную мотивацию продолжать её.

А теперь собственно представляю вам верхнеуровневое описание методики разработки информационных систем "Эргономичный подход" версии 1.0 milestone 1.

Эргономичный подход v1.0M1

Мотивация

Люди ещё в 70ых годах выяснили, что основным источником проблем (и как следствие увеличения стоимости) в развитии программ является сцепленность (coupling). Тем не менее сейчас в мейнстримном подходе к разработке существует множество практик, которые упрощают решение задачи в моменте за счёт увеличения сцепленности системы. Такие практики я называю тактическим программированием. Следствием тактического программирования являются избыточная трудоёмкость внесения изменений в систему, сопряжённая с большим количеством регрессий. Что, в свою очередь, ведёт к увеличению итоговой стоимости разработки.

Для того чтобы свести к минимуму количество проблем в своих проектах (а заодно и их стоимость для заказчиков), я нашёл альтернативные практики, которые минимизируют сцепленность системы. И хотя сведение сцепленности к минимуму часто требует дополнительных усилий по проектированию и кодированию, взамен оно решает описанные выше проблемы и уменьшает итоговую стоимость разработки.

Объединив и подогнав друг к другу эти практики, я получил стратегическую методику разработки програм - Эргономичный подход.

У меня (и не только) есть эмпирическое ощущение, что инвестиции в дизайн начинают окупаться уже через два-три месяца от старта проекта. А спустя примерно шесть месяцев "стратегические проекты" уверенно обгоняют "тактические проекты" по количеству реализованной функциональности.

Основные отличия от мейнстримного подхода

Большая часть практик Эргономичного подхода давно известна и продвигается многими известными инженерами и учёными:

  1. Для моделирования информации в ЭП используются агрегаты из DDD, вместо связного графа объектов
  2. ЭП предполагает неизменяемую модель данных и эпохальную модель времени из ФП, вместо императивной изменяемой модели
  3. ЭП предполагает фокус на чистых функциях из ФП, вместо повсеместного императивного программирования
  4. Как следствие первых трёх пунктов - ЭП требует использования Spring Data JDBC, вместо Spring Data JPA
  5. В рамках ЭП выполняется функциональная декомпозиция из Чистой архитектуры и DDD, вместо механической декомпозиции по техническим аспектам кода
  6. ЭП предполагает тестирование сценариев использования из классического подхода Бека, вместо тестирования отдельных классов и методов
  7. В рамках ЭП выполняется тестирование модулей в конфигурации, приближенной к боевой из классического подхода, вместо мокирования всех зависимостей
  8. ЭП разделяет код на чистое ядро и императивную оболочку из ФП (и ДДД, и Чистой архитектуры), вместо разделения кода на слои представления, бизнес-логики и данных
  9. ЭП предполагает "крафтовое" управление зависимостями, вместо использования неуправляемого глобального Spring Component Scan
  10. В рамках ЭП выполняется "крафтовая" настройка инфраструктуры, вместо неуправляемого глобального сканироваия Spring Auto Configuraions

Многие из этих пунктов подробнее раскрыты в посте "Что я делаю не так".

Рассмотрим саму методику.

Методика

Глобально разработка по Эргономичному подходу состоит из привычных этапов - системная аналитика, проектирование и реализация.

Системная аналитика

Выполнять системную аналитику проще всего на основе сценариев использования и мокапов UI. Но эти артефакты создаются далеко не всегда, поэтому требования зачастую приходится выуживать из заказчика или эксперта напрямую.

На этапе системной аналитики строится ER-диаграмма модели информации системы, определяется список необходимых интеграций и проектируется API системы. Все три этих артефакта сильно связаны между собой и создаются параллельно в процессе разбора и проработки требований. Сейчас API системы, как правило, выполняется в REST-стиле.

После этого создаётся диаграмма эффектов. Диаграмма эффектов - это моё изобретение, которое объединяет в себе информацию из предыдущих трёх артефактов и является исходным материалом для этапа проектирования.

Построение диаграммы эффектов начинается с того, что на неё помещаются все методы API в качестве операций системы, а все множества сущностей и интеграции - в качестве ресурсов. Затем для каждой операции определяются события, её инициирующие, после чего определяются и обозначаются связи между событиями, операциями и требуемыми для них ресурсами.

Гранулярность отдельных сущностей является слишком мелкой и работать с ними несподручно, поэтому после построения диаграммы эффектов необходимо объединить их в агрегаты и по мере обнаружения агрегатов заменить ресурсы кластеров соответствующих сущностей на ресурс агрегата.

Закончив диаграмму эффектов, можно переходить к этапу проектирования модулей системы.

Проектирование

Этап проектирования начинается с того, что диаграмма эффектов декомпозируется на модули таким образом чтобы:

  1. инкапсулировать модификации ресурсов в одном модуле
  2. в целом минимизировать любые обращения операций к ресурсам других модулей

Построив первую декомпозицию на базе эффектов, надо без фанатизма попытаться построить список сокрытия предположений из декомпозиции по Парнасу. Если в результате находится что-то существенное - вынести это в отдельный модуль и отразить его на диаграмме модулей.

После этого получившуюся декомпозицию необходимо проверить на предмет соответствия принципам:

Затем необходимо определить и скрыть подмодули. Модуль является подмодулем если он:

  1. Используется только одним модулем
  2. Теряет всякий смысл при удалении этого модуля

Получив "достаточно хорошую" декомпозицию, можно переходить к этапу реализации.

Реализация

Реализацию необходимо начинать с того, чтобы создать по пакету для каждого модуля, а в пакете как минимум классы Spring конфигурации и сервиса без реализации.

Следующим шагом необходимо написать тест на сценарий использования, выбранный для реализации в данный момент. Сценарный тест - это тест, который работает через внешний интерфейс системы (HTTP, как правило) и симулирует работу клиента. На данном этапе, естественно, тест не проходит.

Если реализуется первый сценарий использования системы, то необходимо создать точку входа - класс с методом main и корневой Spring конфигурацией. При этом необходимо воздержаться от использования глобального Spring Component Scan и AutoConfiguration - приложение "собирается" посредством импорта конфигураций всех модулей в корневой конфигурации приложения. Если модулям нужна инфраструктура (например, подключение к БД) - для этого создаются отдельные конфигурации. Затем эти конфигурации импортируются только в те модули, которым они требуются.

После этого можно переходить к непосредственно реализации операций системы, задействованных в непроходящем на данный момент тесте. Для этого, как правило, надо реализовать два класса ресурса - отдельные элементы (сущности, DTO, события и т.п.) и их коллекции (репозитории, REST-клиенты, топики в очередях событий). Эти классы лучше делать internal/package private. Но если отдельные элементы надо выдавать наружу как есть, нет ничего страшного в том, чтобы выставить их в публичный интерфейс модуля.

В реализации операций необходимо придерживаться структуры ориентированной на трансформации и функциональной архитектуры. Из этого следует несколько ограничений:

  1. Отдельные элементы необходимо реализовывать в виде неизменяемых структур данных
  2. Коллекции элементов необходимо реализовывать декларативно или максимально прямолинейно, идеальная коллекция элементов - интерфейс Spring Data репозитория
  3. В классе сервиса остаётся только верхнеуровневое управление потоком данных
  4. Преобразования (если они есть) необходимо выносить либо в классы отдельных элементов, либо в чистые функции ядра

После того как сценарный тест прошёл, необходимо прислушаться к себе - достаточно ли сценарного теста, для того чтобы спокойно зарелизать сценарий в прод? Если недостаточно - дополнить тестовый набор. Дополнять можно компонентными и юнит-тестами. Предпочтение лучше отдавать компонентным тестам, переходя к юнит-тестам в случае, если, по вашему мнению, повышение сцепленности тестов нивелируется высокой скоростью их написания и выполнения, а также стабильностью и процентом покрытия. При этом использование моков классов в любом виде тестов не допускается.

При наличии возможности, внешние системы также лучше использовать полнофункциональные. Мокирование внешней системы допускается, если нет принципиальной возможности её использования или же использование в тестировании влечёт существенные прямые расходы (платное АПИ) или нестабильность тестов. При этом лучше отдавать предпочтение механизмам мокирования на уровне процессов, таких как WireMock.

Получив достаточную степень уверенности, можно отдавать код на ревью и, вмёржив его, переходить к реализации следующего сценария.

Заключение

Реализовав все сценарии таким образом, вы получите систему с надёжным покрытием тестами и низкой сцепленностью. Это существенно сократит итоговую стоимость реализации системы и количество отрицательных эмоций у разработчиков, пользователей и заказчиков.

Если вы готовы внедрить Эргономичный подход у себя в компании или проекте, напишите мне:

И мы обязательно найдём взаимовыгодный способ сотрудничества.