Чистая архитектура¶
«Удаление радиоактивной пыли с одежды, обуви и средств защиты производится вытряхиванием, выколачиванием, обметанием и обтиранием».
МЧС России.
Обычно принципы действия работы чистой архитектуры принято разбирать, основываясь на симпатичной круговой диаграмме (ранее использовался даже термин Onion Architecture, ныне подзабытый), продвигаясь от центра к внешним границам и разъясняя трансформации, происходящие по пути. Круговые диаграммы выглядят эстетично и вселяют какую-то особую надежду на своё непорочное совершенство и непоколебимую устойчивость. На части иллюстраций многослойный кружочек еще и разрезают, выворачивая его сначала в полукружие, а потом и в линейную нарезку.
Давайте попробуем всё немного упростить и обойтись вовсе без графических иллюстраций.
Для этого рассмотрим сначала обратный пример — «грязную» архитектуру.
Итак, мы хотим создать совершенно новую программу. Не будем усложнять пример, мы хотим написать не картографический сервис, конкурент Google Maps и Яндекс Карт, а несложную программу-игрушку «Крестики-Нолики» на C# с простейшим CLI (Command Line Interface), когда ввод хода пользователя и вывод результирующего игрового поля происходит не при помощи графического интерфейса, а с помощью скромного bash. Коротенькая программа написана, хорошо работает и радует своего владельца.
Программист, который разработал «Крестики-Нолики», был человеком разносторонним и решил переделать интерфейс программы с CLI на WPF. Переделка заняла не очень много времени и очень скоро новая программа в Windows-стиле радовала своего создателя. Еще через некоторое время разработчик при помощи Blazor сделал себе такую же игрушку, но с web-интерфейсом, чтобы можно было скоротать время при помощи браузера.
Спустя некоторое время программист обнаружил небольшую ошибку в коде программы. При некоторых, нечастых комбинациях игрового поля игра вела себя некорректно и выдавала неверный результат. К счастью, ошибка была простой и её исправление во всех трех программах с тремя разными интерфейсами (CLI, оконный и web) не заняло много времени.
Какие основные отрицательные моменты у продемонстрированного подхода, у «грязной» архитектуры?
Первое — логика самой игры перемешана с логикой интерфейса. При смене интерфейса (сущностью, внешней относительно игрового движка) необходимо переделывать всё. Второе — при расширении функционала программы, при исправлении ошибок, даже при простом рефакторинге нужно править некоторые одинаковые сущности в нескольких местах, что, разумеется, никак не способствует ни скорости, ни качеству работы. Третье — ядро игры, её логика, вынуждена знать, как работают те или иные интерфейсы, просто для нормального взаимодействия с ними, ведь это именно ядро решает, что увидит пользователь в рамках того или иного интерфейса в каждый конкретный момент игры.
Если эти небольшие, на первый взгляд, шероховатости умножить на количество сущностей, эксплуатируемых в рамках одного современного программного продукта и экстраполировать сложность с пет-проекта на реальный бизнес-продукт, то все указанные проблемы вполне могут привести к тому, что существующий проект будет очень дорого или почти невозможно корректировать и модифицировать.
Перейдем к, собственно, чистой архитектуре. Напомню, что чистая архитектура — не панацея и не лекарство от всех проблем. Есть множество вопросов, на которые она отнюдь не даёт ответ, и проектирование ПО с помощью подходов чистой архитектуры вовсе не превращается в формальную пошаговую процедуру. Хотя чистая архитектура декларирует, что сеть и БД являются всего лишь внешним окружением и имеют второстепенную важность, вы, скорее всего, не сможете быстренько заменить PostgreSQL на Redis, а REST на gRPC, если вдруг внезапно этого захотите. Но дополнительные слои абстракций вы получите гарантированно.
Проектирование нашей игры «Крестики-Нолики» чистая архитектура рекомендует начать с выделения так называемой бизнес-логики. Бизнес-логика, если по простому — эта та часть нашего проекта, которая будет сохранена, даже если на свете не будет ни сетей связи, ни баз данных, ни даже компьютеров. Банковский бизнес, торговля, образование, картография и тысячи других придумок человечества (включая, кстати, «Крестики-Нолики») прекрасно работали без CPU, БД и REST, используя бумагу, пергамент, козьи шкуры или клинопись по сырой глине.
Бизнес-логика ничего не знает о внешнем мире, использует удобные именно ей имена переменных, DTO, вычислительные схемы и структуры данных. Бизнес-логика никогда не обращается напрямую к базе данных или к сети, она просто не имеет ни о базах, ни о сетях ни малейшего понятия. Всё, что знает бизнес-логика игры «Крестики-Нолики» — это правила игры в «Крестики-Нолики».
Далее идут так называемые сценарии использования (use cases) — описание поведения системы при взаимодействии с внешними сущностями (или акторами), которые могут быть как людьми, так и другими программами. Сценарии, в свою очередь, знают о бизнес-логике, используют её, но ничего не знают о слое, находящимся выше. Нужно помнить, что сценарии использования сейчас не так актуальны, как раньше, и вместе с UML, RUP и другими сущностями, призванными упростить бизнес-планирование в программировании, потихоньку теряют свою актуальность. Так что вы можете не разделять бизнес-логику и сценарии.
Далее лежит слой интерфейсов. В этом слое происходит преобразование информации из формата, удобного для бизнес-логики и сценариев использования, в формат, удобный для внешних сущностей, таких, как база данных или пользовательский интерфейс.
И, наконец, самый внешний слой — связующее звено с внешним миром, с фреймворками, интерфейсами и базами данных.
Общее количество слоёв может меняться, главное — всегда должно выполняться правило зависимостей: зависимости всегда должны быть направлены внутрь.