В предыдущем параграфе изучили основы чистой архитектуры и принципы её построения. Также познакомились с принципами S.O.L.I.D. Теперь, когда вы понимаете теоретическую базу, возникает практический вопрос: как организовать структуру проекта, чтобы она отражала принципы чистой архитектуры и S.O.L.I.D?
Существует два основных подхода к организации файловой структуры: Layer First
(сначала слои) и Feature First
(сначала фичи). Мы разберёмся, что кроется за этими аббревиатурами, а также рассмотрим преимущества, ограничения и критерии выбора между ними.
Выше мы вбросили слово «фича» — давайте сперва коротко поговорим о том, что это такое. А уже потом вернёмся к организации структуры проекта.
Что такое фича
Этот термин будет часто встречаться в параграфе (а ещё чаще — в процессе разработки), поэтому сперва давайте разберёмся, что же он значит.
Итак, фича
(англ. feature) — это сленговое обозначение функциональности проекта. Например, фичами являются отображение списка заказов, просмотр информации о заказе, формирование нового заказа и так далее.
Если представить проект как некоторую состоящую из блоков структуру, то фичи будут играть роль этих блоков. В свою очередь, фича — это тоже структура, состоящая из блоков, — они реализованы в виде программных классов.
Воспользуемся примером про записную книжку контактов из параграфа про чистую архитектуру.
Фича
записной книжки — это совокупность взаимосвязанных классов UI-interface
, ContractService
, Contract`Database. Каждый класс отвечает за определённую зону ответственности, обусловленную разделением на слои.
Вот как это можно представить на схеме:
Теперь мы готовы ввести определение. Фича
— это набор взаимосвязанных классов, разделённых согласно зонам ответственности.
Итак, с терминологией разобрались. Теперь поговорим, как организовать структуру проекта. Напомним, что подходов два:
-
Подход №1: распределить классы, относящиеся к одной из фич, по папкам в зависимости от того, к каким слоям они относятся (он называется
Layer First
). -
Подход №2: сгруппировать классы, относящиеся к одной фиче, в отдельную папку (это
Feature First
).
Разберём эти подходы подробнее.
Подход №1: Layer First
Тут мы работаем «от слоёв»: проект разбивается на логические слои, каждый из них отвечает за предметную область. Классы фич ложатся в слои по предметной принадлежности.
Чтобы стало понятнее, давайте рассмотрим пример. Предположим, мы решили следовать классической архитектуре MVC(Model — View — Controller, «Модель — Представление —Контроллер»). Для этого необходимо разделить проект на три слоя:
-
Модель отвечает за бизнес-логику и взаимодействие с данными.
-
Представление — за отображение информации и взаимодействие с ней.
-
Контроллер обрабатывает действия пользователя и управляет взаимодействием между моделью и представлением.
Представим, что нам нужно добавить новую фичу в проект со структурой Layer First
. Сперва мы разобьём её на классы по слоям MVC. Получившиеся классы необходимо распределить по директориям в проекте, где каждая директория представляет отдельный слой.
В итоге получается структура, где классы фич распределены по слоям, как на рисунке.
Layer First глазами нового разработчика
Новый разработчик приходит на проект, видит техническую реализацию и чёткую файловую структуру проекта. Сразу видно, на какие логические слои разбит проект, но структура не даёт понимания, что делает приложение.
Особенности подхода
-
Простота разработки. Каждый слой отвечает за свою зону ответственности, что уменьшает сложность разработки. Разработчики легко погружаются в структуру и другие нюансы проекта.
-
Легкость в тестировании. Тут справедлив паттерн «Разделяй и властвуй». Изолированные слои приложения значительно проще тестировать по отдельности, что повышает качество и скорость тестирования.
-
Гибкость. Подход позволяет легко изменять или добавлять новые функции, так как каждый слой можно модифицировать независимо от других.
-
Риск чрезмерного усложнения. Если структура становится слишком сложной, это затрудняет понимание и поддержку кода. Важно найти баланс между сложностью и функциональностью.
-
Сложность реализации. Создание сложной структуры слоёв требует времени и усилий. Необходимо тщательно продумать структуру, чтобы избежать проблем в будущем.
Кому подойдёт (и не подойдёт)
В основу Layer First
ложится контракт (или набор контрактов) о том, как будет формироваться новая фича
— то есть в каких каталогах будут лежать классы, отвечающие за слои фичи.
Основной фокус при использовании Layer First
направлен на адекватное формирование контрактов и на контроль за их соблюдением. Логично предположить, что чем больше штат разработчиков, тем сложнее контролировать соблюдение правил в команде. Следовательно, Layer First
эффективен для малочисленных команд, где все работают с кодом всего приложения. В таких командах разработчикам проще сформулировать контракты, поделиться по слоям и работать независимо, а потом соединить наработки.
Для крупных приложений с распределёнными командами Layer First
не подходит, так как с ростом проекта становится сложнее контролировать соблюдение контрактов. В каждой отдельной команде начинают формулироваться свои собственные контракты. В итоге объединение результатов команд в единое целое становится непростой задачей. Чем сложнее иерархия в команде, тем сложнее контролировать её действия.
Теперь давайте поговорим про Feature First
.
Подход №2: Feature First
Каталоги верхнего уровня проекта называются в соответствии с фичами. Внутри каталогов происходит разделение фичи на слои. Подход разделения на слои зависит от выбранного архитектурного паттерна (MVC, MVP, MVVM и так далее).
Давайте разберём структурный подход аналогично Layer First
— тоже на примере реализации MVC. Вводные данные идентичны. На верхнем уровне иерархии проекта будут фичи Feature1
, Feature2
, Feature 3
и так далее. Каждая фича
состоит из набора каталогов Models
, Views
, Controllers
. Каждый каталог содержит фича-классы
, соответствующие разделению на слои.
Глазами нового разработчика
Открыв проект, новый разработчик видит, какие фичи есть в приложении. Так он быстрее поймёт, что делает код проекта. Однако он не сможет быстро разобраться, как фичи связаны друг с другом и в чём суть приложения для пользователя.
Особенности подхода
-
Фокус на функциональности. Разработка сосредотачивается на реализации конкретных фич продукта. Фичи представляют собой изолированные модули, в которых происходит разделение на слои.
-
Изолированность.
Feature First
позволяет быстро разрабатывать отдельные фичи. Это достигается за счёт разделения ответственности на мелкие команды. Команды могут изменять правила под свои нужды без опасения, что эти изменения как-то повлияют на соседей, так как контракты не распространяются на соседние фичи. Это и плюс, и минус одновременно: с одной стороны, разработчики в рамках одной фичи могут делать что угодно, с другой стороны, это нарушает единую концепцию построения фич проекта. -
Проблемы с интеграцией. Следствие предыдущего пункта. Если каждая команда начинает использовать свои подходы и контракты, это приводит к усложнению процесса интеграции фич в единый модуль или приложение, так как каждая
фича
создана по своим собственным правилам, а свести их к единому знаменателю — задача непростая.
Кому подойдёт (и не подойдёт)
В основу Feature First
ложится контракт о том, как формируются фичи. Разница между подходами заключается в том, что контракт будет действовать внутри фичи, следовательно, контролировать соблюдение контракта необходимо также внутри фичи.
Feature First
обеспечивает оптимальную структуру для разработки проекта распределёнными командами. Такой подход специфичен для крупных проектов, где за каждой фичей закреплена отдельная команда. Контракты могут корректироваться под нужды команды, и это никак не повлияет на соседние фичи. Но на практике фичи могут иметь разную внутреннюю организацию. Это не проблема, пока не возникают конфликтующие зависимости или фичи не навязывают свою структуру другим.
Однако полная автономность фич — это риск. Более эффективно подчинять их компонентам/модулям, которые учитывают интересы не только разработчиков, но и бизнеса в целом. Компоненты устанавливают границы, предотвращают конфликты зависимостей и обеспечивают соответствие фич общим целям проекта, без чрезмерного замедления разработки.
Послесловие
Помимо Layer First
и Feature First
, существует более высокий уровень архитектурных решений — уровень компонентов. На этом уровне принимаются решения, выходящие за рамки обычной разработки, например переход на монорепозиторий, выбор платформ интеграции, организация CI/CD и т. д. Эти решения напрямую не влияют на структуру классов, контроллеров или стейт-менеджеров, но определяют общие правила игры.
На всех уровнях приходится искать компромисс: неоптимальное сегодня решение может стать идеальным завтра. Гибкость и скорость внедрения часто не менее важны, чем чёткая архитектура.
Реально комбинировать разные подходы: Layer First
для стабильных компонентов, Feature First
для более динамичных требований, — при этом подчиняя всё общим правилам на уровне компонентов.
***
Давайте коротко подведём итоги этого параграфа.
-
Мы продолжили погружение в особенности построения архитектуры программного обеспечения (англ. software architecture).
-
Изучили и детально разобрали подходы к построению файловой структуры приложения (
Layer First
иFeature First
). -
Глазами нового разработчика посмотрели на проект, построенный по канонам каждого подхода.
-
Выяснили, что выбор подхода должен основываться на комплексном анализе бизнес-модели, целей, технологий и мнения команды, а также долгосрочных целях.
Благодаря этому вы смогли улучшить навыки проектирования архитектуры, аналитического и стратегического мышления. Это важные навыки, которые помогают вам расти как разработчику и достигать новых высот в профессии.