Перейти к основному содержимому

Разбиение приложения

Group: Layers#

Первый уровень разделения: по скоупу влияния модуля

Самопроверка

"К какому слою приложения относится модуль?"

└── src/    ├── app/                    # Инициализирующая логика приложения    ├── processes/              # (Опц.) Процессы приложения, протекающие над страницами    ├── pages/                  # Страницы приложения    ├── features/               # Ключевая функциональность приложения    ├── entities/               # Бизнес-сущности    └── shared/                 # Переиспользуемые модули

Порядок слоев#

Если посмотреть на порядок слоев - то можно выделить две закономерности:

По уровню знания/ответственности#

app > processes > pages > features > entities > shared

Модуль "знает" только про себя и нижележащие модули, но не выше лежащие

Это же влияет и на разрешенные импорты

По уровню опасности изменений#

shared > entities > features > pages > processes > app

Чем ниже расположен модуль - тем опаснее вносить в него изменения

Т.к. скорее всего он заиспользован во многих вышележащих слоях

Group: Slices#

Второй уровень разделения: по конкретной функциональности БЛ

Методология почти не влияет на этот уровень и многое зависит от конкретного проекта

Самопроверка

"Какую область БЛ затрагивает модуль?"

До этого - надо определится со скоупом влияния (layer)

├── app/|   # Не имеет конкретных слайсов, |   # Т.к. там содержится мета-логика над проектом и его инициализации├── processes/|   # Слайсы для реализации процессов на страницах|   ├── payment|   ├── auth|   ├── quick-tour|   └── ...├── pages/|   # Слайсы для реализации страниц приложения|   # При этом, в силу специфики роутинга - могут вкладываться друг в друга|   ├── profile|   ├── sign-up|   ├── feed|   └── ...├── features/|   # Слайсы для реализации конкретной функциональности на страницах|   ├── auth-by-phone|   ├── inline-post|   └── ...├── entities/|   # Слайсы бизнес-сущностей для реализации более сложной БЛ|   ├── viewer|   ├── posts|   ├── i18n|   └── ...├── shared/|    # Не имеет конкретных слайсов|    # Представляет собой скорее набор общеиспользуемых сегментов, без привязки к БЛ

Правила#

Поскольку слайс представляет собой конкретный уровень абстракции, то методология обязана наложить на него определенные правила

Low Coupling & High Cohesion#

Слайсы одного слоя не могут использовать друг друга напрямую, а их взаимодействие и композиция должны определяться на более верхнем слое, относительно их текущего

features/baz/ui.tsx
// Плохо: фича импортит другую фичу (слайсы одного слоя)import { Bar } from "features/bar"
function Baz({ foo, ...barProps}) {    ...    <Bar {...barProps} />}
pages/foo/ui.tsx
// Хорошо: фичи компонуются на странице (вышележащий слой)import { Baz } from "features/baz"import { Bar } from "features/bar"
function Foo() {    ...    <Baz {...fooProps}>        <Bar {...barProps} />    </Baz>}

Grouping#

  • В большинстве случаев следует избегать вложенности в слайсах, а использовать лишь структурную группировку по папкам, без дополнительной связующей логики

    features/order/           # Группа фич   ├── add-to-cart        # Полноценная фича   ├── total-info         # Полноценная фича-  ├── model.ts           # Общая логика для группы-  ├── hooks.ts           # Общие хуки для группы   └── index.ts           # Публичный API с реэкспортом фич
  • При этом некоторые слои (например pages), изначально требуют вложенности из-за требований проекта / фреймворка

    pages/   ├── order/   |    ├── cart/   |    ├── checkout/   |    |    ├── delivery/   |    |    └── payment/   |    ├── result/   |    └── index.tsx   ├── auth/   |    ├── sign-in/   |    └── sign-up/   ├── home/   ├── catalog/
Важно

Следует по-максимуму избегать вложенных слайсов, но даже если приходится их использовать (например для pages) нужно связывать их явным образом, во избежание непредвиденных последствий

Group: Segments#

Третий уровень разделения: по назначению модуля в коде и реализации

Самопроверка

"Какую часть тех. реализации логики затрагивает модуль?"

До этого - надо определится со скоупом влияния (слой) и доменной принадлежностью (слайсом)

{layer}/    ├── {slice}/    |   ├── ui/                     # UI-логика (components, ui-widgets, ...)    |   ├── model/                  # Бизнес-логика (store, actions, effects, reducers, ...)    |   ├── lib/                    # Инфраструктурная логика (utils/helpers)    |   ├── config*/                # Конфигурация (проекта / слайса)    |   └── api*/                   # Логика запросов к API (api instances, requests, ...)

При этом, каждый сегмент может быть представлен как в виде файла, так и в виде отдельной директории - в зависимости от сложности и размеров

Ограничения#

Методология разрабатывалась с целью - не ограничивать и не утруждать разработчиков правилами выбора абстракций (хотелось, чтобы любой из сегментов можно было использовать в любом слое)

Однако в результате дискуссий и анализа обширного опыта - было определено, что лучше и практичнее ограничить каждый слой на используемые внутри сегменты.

Общие правила#

  1. Чем выше расположен слой - тем больше он знает про БЛ приложения и наоборот
  2. API логику рекомендуется класть в shared, чтобы логика не распылялась по проекту
    • Как правило - она общая и представлена в виде единых инстансов
    • Edge-case "exceptions": GraphQL, react-query hooks

Применение для слоев#

СлойСодержимоеРазрешенные сегменты
appНе включает в себя слайсы и содержит логику инициализацииИмеющиеся сегменты не совсем подходят, а потому используются обычно /providers (/hoc, ...), /styles и т.д. Очень зависит от проекта и вряд ли решается методологией
processesСлайсы внутри включают в себя только бизнес-логику, без отображения (1)lib model (api)
pagesСлайсы внутри включают в себя ui- и model- композицию различных фичей для конкретной страницыui lib model (api)
featuresСлайсы внутри включают в себя композицию сущностей и реализацию БЛ в модели + отображениеui lib model (api)
entitiesСлайсы внутри представляют разрозненный набор подмодулей для использованияui lib model (api)
sharedСодержит только инфраструктурную логику без БЛ (1)ui lib api

См. также#