Структура приложения React в соответствии с рекомендациями по функциям

Идея этой структуры основана на статье книга о чистой архитектуре

Эта структура не требует использования точно такой же структуры папок, но она показывает основу для хорошей структуры проекта.
Такой подход улучшает

  • тестируемость,
  • вовлечение новых участников в команду — вам не нужно сосредотачиваться на всем приложении, вы можете просто сосредоточиться на отдельных компонентах и ​​не думать об остальном,
  • это уменьшает объем внимания разработчиков — когда вы разрабатываете одну функцию, есть редкий шанс сломать что-то еще.
  • Поддерживайте стабильный путь сложности для огромных приложений — вам не нужно думать или даже знать о других частях вашего приложения, если вы сосредоточены на одном наборе функций.

рис1

Каждая функция/вариант использования/модуль автономна и имеет несколько уровней абстракций: модели, представления, селекторы, страницы и т. д.
Это также зависит от вашего варианта использования, но вы можете иметь доступ к данным (API) как отдельный объект поверх вашего приложения или вы можете иметь его для каждой функции. Пример показывает, что легче координировать доступ к данным из одного места, чем децентрализовать его, но это зависит от вашего приложения.

Тот факт, что у нас есть все файлы компонентов, связанные с конкретной функцией, в одной папке, не означает, что у нас не должно быть разделения задач. Каждая функция должна иметь уровни абстракции в зависимости от ее сложности, но вы не должны использовать белый связанный код только потому, что это одна функция.

Структура проекта

Высокоуровневая структура проекта.

api
types
modules
\
 |- auth
  \
   |- atoms
   |- molecules
   |- organisms
   |- pages
   |- services
   |- state
   |- selectors
 |- users
 |- todos
 |- dashboard
libs
\
 |- charts
 |- table
 |- validation
 |- file-upload
 |- calendar
 |- pagination
ui
\
 |- atoms
 |- molecules
 |- organisms
 |- pages
 |- templates
 |- theme
// rest of the files for some setup
App
index
setup

библиотеки

эта папка предназначена для хранения файлов без какого-либо отношения к проекту
Правила библиотек.

  1. Они не должны включать код, специфичный для проекта.
  2. Они не должны полагаться на специфичный для проекта код, константы, маршрутизатор и т. д. Код в lib должен полагаться на внешнюю конфигурацию.
  3. Код в библиотеках должен быть хорошо документирован и иметь покрытие кода 80+.
  4. Другие модули не должны использовать внутренние компоненты libs без крайней необходимости.
  5. Либы должны иметь как можно меньше внешних зависимостей
    например, нормально иметь эмоцию/lodash/moment/d3 в качестве зависимости, но лучше пропустить некоторые конкретные библиотеки, такие как recharts или react-router, лучше размещать такой код в модулях.
    В общем, если вы можете заставить некоторые функции работать без зависимости или принять их как реквизит, просто сделайте это.
    Если вы чувствуете, что очень важно иметь что-то в качестве зависимости, используйте это.
  6. Библиотеки могут иметь любую структуру папок, но для больших функций лучше следовать атомарной структуре папок или функции разделения.
    Не помещайте код в библиотеки, если он очень мал или очень специфичен для некоторых функций.

В общем, libs — это вторая папка node_modules, но с вашим кодом или некоторым разветвленным кодом из других проектов.
Папка Libs — хорошее место для кода, такого как пользовательские визуализации, компоненты пользовательского интерфейса, такие как слайдер, флажок, таблица. Имейте в виду, что такие компоненты должны иметь настраиваемые свойства стиля.
руководства по разработке ваших компонентов API

Таким образом, вы можете поместить код, если библиотеки, если

  • этот код не имеет отношения к другим модулям и другому коду проекта, кроме папки libs
  • этот код можно повторно использовать во всем проекте независимо от какой-либо функции
  • этот код реализует атомарную функциональность, и эту функциональность можно настроить с помощью аргументов, реквизитов и т. д.
  • этот код имеет readme и покрыт тестами

Рекомендации:

  • Не пытайтесь создать самую маленькую атомарную библиотеку, которую можно будет использовать везде. Этот код по-прежнему достаточно специфичен для вашего проекта и отвечает за некоторые его функции, поэтому этот компонент можно использовать с минимальной конфигурацией, но он не должен быть специфичен ни для одного из наших доменов.
  • Попробуйте сгруппировать свою функциональность. Например, вы создали кучу причудливых элементов управления, таких как флажки, ползунки, ввод чисел и т. д. Будет лучше сгруппировать их вместе как «элементы управления формой», а не рассматривать их как отдельные экземпляры. Другим примером являются диаграммы, у вас есть много аккуратных диаграмм, таких как гистограммы, круговые диаграммы, диаграммы, графики, вероятно, все они используют d3 как зависимость и построены на одних и тех же принципах, поэтому лучше сгруппировать их вместе.

модули (также известные как функции)

Модули — это фрагменты кода, специфичные для предметной области. Модуль может содержать что угодно и иметь любую структуру кода, но для них есть несколько простых правил и рекомендаций.

  1. Модули должны быть самодостаточными и описывать какую-то достаточно большую часть функциональности приложения.
    например, у вас есть новая функция с предупреждениями разных типов. Эти функции включают в себя: компонент для других страниц с указанием статуса и всплывающее окно с кратким списком сработавших предупреждений, таблицу сработавших предупреждений, страницы для управления предупреждениями. Это хороший кандидат на новый модуль. В результате у нас будут компоненты, реализованные в модуле оповещений, а другие модули полагаются на этот модуль.

  2. Модули не должны полагаться на внутренности других модулей. Модуль должен предоставлять простой API для использования в других модулях, например, наш модуль оповещений будет отображать страницу со списком оповещений, компонентами для короткого списка и индикацией, и все.

  3. Вы можете иметь любую структуру папок внутри модуля, но предпочитаете плоскую структуру, а если этот модуль довольно большой, используйте атомарную структуру папок (см. далее).

  4. Форма зависимостей модулей ДЕНЬ использование также может использовать это для обеспечения соблюдения этой политики https://github.com/sverweij/dependency-cruiser
    Атомная структура внутри модулей:
    Каждый модуль может иметь следующую структуру

atoms
molecules
organisms
pages
templates

atoms — это минимальный строительный элемент в вашем модуле, его можно повторно использовать и настраивать ТОЛЬКО с реквизитами
molecules — составные атомы могут быть достаточно большими, как всплывающие окна, или маленькими, как кнопки с загрузкой.
поскольку эта атомарная структура специфична для вашего домена, молекулы могут определять некоторые специфические стили и иметь жестко запрограммированное поведение, но все же они предназначены для повторного использования.
organisms — повторно используемые компоненты с некоторой функциональностью, они могут иметь предопределенное поведение, но они должны быть встраиваемыми в любую часть страницы.
pages — компоненты страниц, которые не предназначены для повторного использования, они просто составляют организмы, молекулы, атомы и шаблоны.
templates — если у вас много отдельных страниц, вы можете перенести функциональность макета в шаблон, иначе этот объект не очень пригоден для использования в домене.

также есть некоторые дополнительные объекты, это сильно зависит от того, что вы делаете

state - description of your domain model state
effects - async operations within your modules
selectors - derived data from your state
services - usually you can put here some utilities or some data transformation
api - method for backend api calls, I prefer to have them at top lvl but feel free to keep them at module lvl
types - type definitions if you have a lot of the across the module

Уи

Специальные компоненты пользовательского интерфейса. Это внутренний пользовательский интерфейс проекта.

  • Все компоненты предназначены для повторного использования.
  • Все компоненты не полагаются на состояние и не имеют зависимостей ни от одного из модулей
  • Компоненты могут иметь код, специфичный для проекта, если это код, связанный со стилями.

Разница между компонентами в папке lib и в папке ui заключается в том, что компоненты в папке ui могут определять стили фирменного стиля и использовать глобальные константы темы, в отличие от компонентов в lib.

руководства по атомарной структуре для пользовательского интерфейса:

Тестирование

Для тестирования у меня есть некоторые строгие требования. Но у меня мало рекомендаций.

Общие рекомендации по тестированию:
Всегда думайте, как будет использоваться ваш компонент, и тестируйте пограничные случаи, не углубляясь во внутренности компонента.

Это имеет смысл для модулей. Потому что каждый модуль, содержащий его, описывает ограниченный набор функций, которые лучше тестировать с помощью своего API и его вывода. Но это не всегда возможно, поэтому иногда нам приходится разбивать функцию на несколько подмножеств по функциональности и тестировать их. Обычно лучше тестировать функциональные возможности с помощью организмов и страниц, а не полагаться на поведение более мелких компонентов.

Старайтесь не полагаться на определенные компоненты и элементы управления в своих тестах и ​​используйте для этого «data-testid». Вы можете установить data-testid на какую-то кнопку, и тогда во время рефакторинга вы сможете свободно перемещать эту кнопку, не нарушая тесты.

Постарайтесь перенести как можно больше логики из пользовательского интерфейса в хуки. И тестовые хуки отдельно. Таким образом, вы можете сделать свои тесты более надежными и устойчивыми к изменениям. Кроме того, это позволяет вам обрабатывать гораздо больше пограничных случаев и получать лучший опыт разработки тестов. Тестирование пользовательского интерфейса по умолчанию является хрупким, но это не означает, что вы не должны его делать или должны делать это неправильно. Нет, это означает, что вы тратите дополнительное время на реализацию хороших тестов пользовательского интерфейса. Из-за этого разделения представления и логики представления (хуков) это хорошая идея для улучшения опыта тестирования.

Ваши тесты должны завершаться неудачей только в том случае, если изменилась функциональность, но не тогда, когда вы меняете имя свойства или заменяете один компонент для кнопки другим.
Тестируйте функциональность компонента, но не его реализацию.

Для библиотек гораздо сложнее следовать этому правилу. Во-первых, потому что вам нужно тестировать больше пограничных случаев, а во-вторых, потому что вам нужно большее покрытие кода. Для библиотек имеет смысл разделить функциональность на более мелкие части и протестировать их изолированно. Конечно, это в дополнение к полному тестированию API компонентов.

Заметки

Вложенные функции

Обычно вы запускаете новые функции с некоторым именем, специфичным для функции или домена, над которым вы работаете, но по мере роста вашего приложения вы получаете все больше и больше кода, который вы хотите правильно структурировать с лучшей декомпозицией. Есть много способов сделать это, вы можете делать вложенные функции, вы можете вкладывать свои организмы и т. д. Но все это приводит к плохой структуре кода и плохой обнаруживаемости кода. Мои рекомендации к этому:

  1. Всегда ищите код, который можно переместить в библиотеки или компоненты, например, это могут быть диаграммы, какие-то сложные виртуализированные списки, какая-то математика, утилиты данных, алгоритмы. Вы просто можете сделать лучшую декомпозицию и переместить некоторый код, который не связан ни с одним доменом, в библиотеки/компоненты и избавиться от накладных расходов кода в вашем проекте. Но будьте осторожны и не перемещайте код, привязанный к вашей функции, в общий код.
    Другие примеры: это может быть что-то вроде сложных элементов управления формы (перенос, выбор, предложения) или общие диаграммы (графики, столбцы и т. д.).
  2. Другой подход состоит в том, чтобы сделать некоторую параллельную функцию на том же уровне, что и основная функция, например.
    у вас есть посты и компонент галереи постов, который довольно большой и довольно специфичный для вашего приложения, что вы можете сделать, он создает подобную структуру
features/
 - posts
 - posts-gallery

В результате у вас есть отдельная фича, но в то же время видно, что она подтянута к основной фиче. Такой подход приводит к лучшей обнаруживаемости кода и более легкому рефакторингу. Если вы проведете рефакторинг постов, вы, вероятно, проверите, как работает галерея постов прямо сейчас и наоборот.
Одно замечание для этого подхода: лучше делать ленивый импорт вложенной функции.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *