Ранее, я несколько раз упоминал об особом типе архитектуры, которую я называю луковой (onion architecture). Эта архитектура идеально подходит для приложений с длительным жизненным циклом и сложной бизнес логикой. Я считаю, что ее использование в подобных проектах приводит к превосходным результатам, в следствии изначально заложенного в архитектуру акцента на разделение различных аспектов приложения. В луковой архитектуре уделяется особое внимание к описанию поведения системы в терминах контрактного программирования и выносу инфраструктурного кода во внешние модули. На диаграмме ниже, вы можете видеть графическое представление традиционной “многослойной” архитектуры. Это очень популярный подход, использующийся в различных вариациях во множестве виденных мной проектов.
Каждый следующий слой тесно связан с соседними и зависит от используемых инфраструктурных модулей и сервисов. Основной недостаток многослойной архитектуры в порождаемой ею высокой связанности. Как мы знаем, худшим проявлением высокой связанности является проникновение элементов UI и бизнес логики в слой доступа к данным. В проектах, использующих многослойную архитектуру, UI остается тесно связанным со слоем доступа к данным. Переходящая зависимость остается зависимостью. Слой UI не может функционировать отдельно от слоя бизнес логики, также как и слой бизнес логики не способен функционировать отдельно от слоя доступа к данным. Я намеренно игнорирую вопросы связанные с инфраструктурой, поскольку инфраструктура склонна меняться от проекта к проекту. Слой доступа к данным постоянно меняется. Исторически, подходы к работе с данными обновляются по крайней мере раз в три года. Поэтому, в приложениях с длительным жизненным циклом, нужно быть готовым к тому, что через три года придется вносить правки в слой доступа к данным. Если высокая связанность мешает заменять устаревшие модули независимо друг от друга, вся система может перейти в состояние упадка и команде ничего не останется, кроме как полностью ее переписать.
Я предлагаю новый подход к построению приложений. Если быть честным, он не совсем новый, но я предлагаю дать ему имя и закрепить в одном ряду с другими архитектурными паттернами. Паттерны полезны, поскольку предоставляют разработчикам общедоступный словарь для упрощения коммуникации. У луковой архитектуры множество аспектов, и нам необходимо дать ей название для более эффективной коммуникации. На диаграмме ниже изображена луковая архитектура.
Ее ключевая особенность в управлении связанностью. Фундаментальное правило — любой модуль приложения может зависить от более близких к центру луковицы модулей, но не может зависить от более дальних. Иными словами, любая связанность должна быть направлена к центру луковицы.
Луковая архитектура тяготеет к ООП и ставит объекты превыше любых других аспектов приложения. В самом центре находится доменная модель приложения. Вокруг доменной модели находятся другие слои с дополнительной бизнес логикой. Количество слоев в приложении может меняться, но доменная модель должна всегда оставаться в самом центре и, поскольку любая связанность направлена к центру, доменная модель связана только сама с собой. Первый слой вокруг доменной модели, это то место где обычно размещаются интерфейсы репозиториев, обеспечивающих сохранение и получение объектов. Необходимо отметить, что сам процесс сохранения объектов находится вне ядра приложения, поскольку этот процесс обычно включает в себя взаимодействие с сервером баз данных, а в центре ядра находятся только интерфейсы. На внешнем радиусе приложения находятся UI, инфраструктура и тесты. Внешний слой зарезервирован за элементами которые меняются часто. Эти элементы должны быть изолированы от ядра приложения. На самом краю, находятся классы реализующие интерфейсы репозиториев. Эти классы тесно связаны с конкретной технологией доступа к данным и именно поэтому должны быть вынесены за пределы ядра приложения.
Луковая архитектура основана на принципе инверсии зависимостей. Приложению для работы необходимы реализации расположенных в ядре интерфейсов, а поскольку конкретные реализации находятся на внешнем радиусе приложения, приложению также необходим и механизм для инжектирования зависимостей.
База данных распологается не в центре приложения. Перенос механизма по работе с базой данных из ядра в отдельный модуль, может оказаться серьезным шагом для людей, мыслящих о приложениях исключительно в контексте взаимодействия с источниками данных. Приложения конечно же могут использовать базы данных для сохранения объектов, но только через прослойку инфраструктурного кода, реализующего интерфейсы используемые в ядре приложения. Разрыв связанности между приложением и базой данных, файловой системой и т.д. уменьшает стоимость поддержки приложения на всем протяжении его жизненного цикла.
Alistair Cockburn в своем блоге как-то писал на тему Гексагональной архитектуры (Hexagonal architecture). Гексагональная и луковая архитектура основаны на общих принципах: перенос инфраструктуры во внешние модули и написание адаптеров к ним. Я планирую написать больше на тему использованя луковой архитектуры при построении приложений корпоративного уровня. Я по-прежнему остаюсь в этой среде и продолжу рассуждения на тему луковой архитектуры в этом контексте.
От переводчика. Этот пост первый из цикла постов Джеффри Палермо, посвященных луковой архитектуре. Если у хабрасообщества есть интерес, я могу сделать перевод и остальных частей.
Автор: steamru