Привет, Хабр! Зачем нужна архитектура приложения и какие цели она должна выполнять?
Разработка архитектуры ПО - это искусство и наука проектирования программного обеспечения таким образом, чтобы оно удовлетворяло всем заявленным требованиям, а также обеспечивало максимальную простоту доработки, развертывания и масштабирования приложения.
Архитектура должна помогать нам писать максимально гибкий код, который мы сможем расширять с приходом новых функций, а не переписывать с нуля.
У нас до сих пор есть микросервисы, в которых вся логика находится в одном классе, состоящем из более чем 1000 строк. Я понимаю, что поддерживать их намного проще. В них мало объектов и нет абстракций, что уменьшает когнитивную сложность.
Вот например Microsoft предлагают монолитную структуру кода в одной сборке, как одно из решений.
При проектировании архитектуры следует учитывать, что нет универсальной архитектуры, подходящей для всех приложений. Вы можете однажды обнаружить, что натягиваете сову на глобус. Однако, в то же время, существуют общие принципы и подходы, которые могут быть применены к различным проектам и помогут создать эффективную и масштабируемую архитектуру.
Все эти принципы и подходы предлагают разделять приложение на несколько слоев, каждый из которых отвечает за свою конкретную задачу. Часто принято считать что чем больше слоев тем лучше. На самом деле все склоняется к разделению между бизнес-логикой и инфраструктурой, что позволяет легко менять и модифицировать каждый слой отдельно.
💡 Разделение между бизнес-логикой и инфраструктурой
Посмотрим на представление Чистой архитектуры приложения ASP.NET Core от Microsoft
Выделены три блока: Ядро, Инфраструктура и Фреймворк, который их объединяет. Обратите внимание, что нет явного слоя представления. Его компоненты находятся рядом с контейнером зависимостей и конвейером обработки запросов. Если мы еще вспомним, что все современные системы делятся на фронтенд и бэкенд, то API может содержать только контроллеры.
💭Будь проще
Python является простым и гибким языком программирования, который подходит для разработки различных приложений и систем. Он имеет свою философию, известную как "Дзен Пайтона", которая включает в себя ряд принципов и рекомендаций, выделю два:
💡 Явное лучше, чем неявное. Простое лучше, чем сложное.
Когда вы только написали код, его логика находится в вашем сознании. Вы знаете, где начинаются все цепочки поведения и что они используют по пути. Но когда другой разработчик или вы сам, но через какое-то время, будете рассматривать этот код, ему надо будет построить все эти цепочки трансформаций в своей голове.
Не следует лишний раз создавать абстракции, если можно обойтись без них. Например, если ядро доступно для всех слоев, тогда можно использовать явный тип из него. В Trunk Based Development используются абстракций во время работы над фичей, а после завершения задачи абстракция удаляется.
Не создавайте дублирующие Model, Entity, DTO, наследуя их от абстракции. Создайте модель сразу в ядре и используйте ее для всех слоев, если она нигде не отличается. Этого достаточно для прототипа. Когда продукт-менеджер захочет добавить новую функцию в базу, тогда уже будет создана сущность Entity и абстракции. Однако, стоит заметить, что в 80% случаев проекты не продвигаются дальше этапа прототипа.
Не следует сразу делать полное деление слоев и структуры папок по слоям. Важно понимать разницу между логическими слоями и физическими, чтобы правильно организовать код. Не следует слишком детально разделять физические слои по разным библиотекам, поскольку это может привести к излишней сложности и перегруженности кода.
Используй анемичные модели, они проще в восприятии, и легче декомпозируются. Для ярых поклонников можно унаследовать действия от свойств. И получим одну богатую модель и одну анемичную.
☝️С чего начать?
Чистая архитектура говорит про сценарии. Заказчики чаще всего используют блок-схемы и диаграммы последовательности для обозначения своих мыслей, что делает легче мыслить сценарии использования (use cases). В DDD используется концепция ubiquitous language - единого языка. Давайте использовать это как наш единый язык.
💡 Начинать систему надо со сценариев(use cases)
В Use case можно вызывать другие Use case. Важно, что все взаимодействия происходят через сценарии, которые требует бизнес. Далее все сценарии вызывают код инфраструктуры через абстракции.
Имутабельность и чистые функции. Все сценарии не имеют состояния, они лишь являются инструкцией для запроса. Чистые функции имеют маленький порог входа, каждый junior может разобраться и внести правки в бизнес-процесс.
Если проект начнет расти, то можно создавать слои, но эти слои будут расширениями существующих, а не промежуточными. Т.е. если у нас появятся слои Persistence, Domain, Application. То это будут слои расширения для существующих т.е. Infrastructure.Persistence, Core.Domain, Core.Application.
💡 Никто из инфраструктуры не может использовать Use case!
Избегать вызова сценариев непосредственно из уровня инфраструктуры. Это может привести к потере контроля над кодом
🏅Итоги
Чистая архитектура - это важный аспект разработки любого приложения, однако это вектор направления по которому следует идти в процессе жизненного цикла приложения, а не пытаться все реализовать на старте проекта. Это позволит убедиться, что все аспекты приложения учитываются и упрощают процесс разработки.
Главное соблюдать логическое деление бизнес логики и инфраструктурного кода, а не физическое разделение слоев. Для этого лучше сразу использовать use cases.
Ваша архитектура приложения может стать как в примере у Дяди Боба, но это должно прийти с требованиям к проекту.
Важно помнить, что добавление абстракций и паттернов должно быть основано на потребностях приложения, а не на их доступности. Вместо того, чтобы добавлять их на этапе создания приложения, следует добавлять их по мере необходимости.