Beta Документация для beta‑теста, возможны ошибки и неточности.
Перейти к содержимому

Common

Common — уровень, определяющий сущности для общего переиспользования. Это сущности, которые не привязаны к конкретной бизнес-логике и могут использоваться в любом месте проекта. А также одиночные сущности которые сложно причислить к какому-то конкретному модулю.

Важно

Несмотря на сходство common из FEOD и shared из FSD, они имеют разные цели и правила использования. Подробнее об отличии еще будет ниже.

Посколько это блок наибольших заблуждений, то тут будет множество отсылок на FSD, но если вы не знакомы с FSD, то это не проблема, пометки в первую очередь стоят чтобы именно привыкшие к FSD разработчики не путали common и shared из FSD.

Возможное альтернативное название:

  • shared — привычнее для пользователей FSD. Но стоит осознавать, что это не совсем то же самое что это не тот же самый shared из FSD.

Важность уровня

На самом деле данный уровень является опциональным и утилитарным. Он решает задачу: «куда положить сущности которые не привязаны ни к чему не привязаны и слишком мелкие чтобы стать полноценным модулем».

На самом деле common решается множество задач которые могут быть затруднительными если от него избавиться:

модуль ради модуля

Благодаря данному слою множество модулей которые вмещались бы в 1 файл и сложно объединить в один модуль будут иметь своё место. Вам не придется создавать новый модуль ради одной сущности.

Благодаря данному слою мы можем сильно сократить список модулей в проекте спася его от загрязнения. При этом правила для common достаточно строгие и защищают common от загрязнения самого себя.

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

Плавное привыкание после FSD

После перехода с FSD многие разработчики могут испытывать дискомфорт от отсутствия shared слоя в модульной архитекутре. common является разумным компромиссом между модульной архитектурой и слоистой.

Как понять, что сущность — это часть Common?

  • Это не привязанная к конкретным бизнес-фичам логика
    • Если сущность привязана к бизнес-фиче, то лучше переместить её в соответствующий модуль
  • Вмещается в один файл
    • Если сущности необходимо разделить на несколько файлов, то лучше переместить её в отдельный модуль
  • В теории может быть использована в любом месте
    • Если это не универсальная сущность, то лучше переместить её в соответствующий модуль
  • Ей не нужно взаимодействовать с сущностями уровня Module
    • Если сущность взаимодействует с сущностями уровня Module, то лучше переместить её в соответствующий модуль или использовать IoC

Важные правила

  • В отдельных случаях возможно использование двух и более файлов на одну сущность, но рекомендуется этого избегать и предпочитать в этом случае Module
  • Common могут взаимно использовать друг друга, пока это не создаёт слишком жёсткие связи
  • Сущности в Common не могут использовать индексные файлы

Почему запрещено использовать индексные файлы?

  • Это создаёт ненужные файлы, которые не несут в себе смысловой нагрузки
  • Меньше ненужных действий для создания сущности (не нужно создавать индексный файл и добавлять в него все импорты + следить за этим)
  • Это мотивирует при необходимости использовать модули
  • Это косвенно вынуждает держать уровень common тонким и не засорять его

Структура

  • common
    • ui
      • Button.vue
      • Input.vue
      • index
    • composables
      • useDebounce.ts
      • useLocalStorage.ts
      • index
    • utilities
      • formatDate.ts
      • validateEmail.ts
      • index
    • types
      • api.types.ts
      • index

Примеры сущностей Common

Единый пример собирает UI, композаблы, утилиты и типы в одном дереве и наборе файлов.

  • common
    • ui
    • composables
    • utilities
    • types

Выберите файл в дереве слева

Использование Common в проекте

Правильный импорт

ts
// OK Правильно
import Button from '@/common/ui/Button.vue'
import { formatDate } from '@/common/utilities/formatDate'
import { useDebounce } from '@/common/composables/useDebounce'

// ❌ Неправильно (индексные файлы запрещены)
import { Button, formatDate, useDebounce } from '@/common'

Использование IoC для работы с модулями

Если common сущность нуждается в функциональности из модуля, используйте IoC:

ts
interface NotificationService {
  show(message: string): void
}

export function useNotification(service?: NotificationService) {
  const notify = service?.show || console.log
  
  return {
    notify
  }
}

FAQ

Строгий ли запрет на index.ts в Common?

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

Это решение было принято для нескольких целей:

  • Сборщику придётся меньше бороться с barrel-файлами, что улучшает производительность проекта. В случае модулей мы вынуждены использовать index.ts-файлы, чтобы обеспечить публичный интерфейс модуля, но у common такой необходимости нет
  • Это мотивирует при необходимости использовать модули, так как связывать сущности между собой становится сложнее
  • Это косвенно вынуждает держать уровень common тонким и не засорять его, так как при разрастании импорты из common становятся более громоздкими

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

Когда выносить сущности из Common?

  • Когда несколько common-сущностей сцепливаются между собой, то, скорее всего, они реализуют какую-то общую задачу. Их лучше вынести в модуль.
  • Когда сущность привязывается плотно к бизнес-логике. Например, функция регистрации вряд ли будет Common.
  • Когда сущность вынуждена использовать сущности из других уровней, кроме Global

Это нормально если по мере жизни проекта вы будете выносить эти сущности в модули. Во многом это и есть задача common слоя.

Как использовать сущность из модуля, если это необходимо в Common?

Для этого можно применить IoC (Inversion of Control). И прокинуть необходимую сущность в Common.

UI-модуль и Common

Так как UI предполагает между собой тесно связанные элементы, то лучше держать его в отдельном модуле, а common оставить для примитивных сущностей. Однако нам часто необходимо использовать компоненты из UI-модуля в common-уровне. В этом случае у нас есть несколько путей: IoC или же исключение из правила про запрет на импорты modules из common. Да, это плохо. Но лучшее — враг хорошего: порой лучше поступиться, чем значительно усложнить проект. Либо же оформить его как псевдо-модуль внутри common.

Common как модуль

Может возникнуть вопрос, почему Common не является модулем сам по себе?

  • Common не является модулем, потому что он не предназначен для взаимодействия с другими модулями.
  • Common предназначен для хранения общих сущностей, которые могут быть использованы в любом месте проекта без особой специфики.
  • Для common действуют особые правила экспорта, противоречащие правилам модулей (запрет на использование индексных файлов).
  • Это позволяет поддерживать количество модулей минимально необходимым для проекта.
  • Дополнительные правила в common служат противовесом для загрязнения общего уровня.

Однако существуют модификации FEOD, которые позволяют common быть модулем. Подробнее об этом вы можете прочитать в разделе Common Module.

Design System — это Common?

  • Design System не вполне укладывается в common-уровень.
  • Однако из-за сильной связанности элементов между собой, лучше держать его в отдельном модуле, а common оставить для примитивных сущностей.
  • Однако если это просто набор базовых компонентов для отображения, не образующих дизайн-систему, то это может быть Common.
  • Если вы используете стороннюю библиотеку компонентов, то вы вполне можете держать здесь переопределения компонентов и прочие донастройки.

Отличия common из FEOD и shared из FSD

Самое важное отличие, что ориентированный на слои абстракции FSD использует common как самый базовый уровень из которого строятся остальные. В это время FEOD ориентирован на модули и использует common как отправную точку для того, что еще не сформировалось в модуль.

  • Common из FEOD и shared из FSD являются уровнем для общих сущностей, которые могут быть использованы в любом месте проекта без особой специфики.
  • Common из FEOD и shared из FSD могут взаимно использовать друг друга, пока это не создаёт слишком жёсткие связи.
  • Common из FEOD и shared из FSD имеют запрет на импорты из других уровней.

Далее отличия:

  • Common из FEOD имеет запрет на использование индексных файлов, в то время как shared из FSD наоборот заставляет их использовать.
  • Common из FEOD не пытается быть отдельным слоем абстракции, в то время как shared из FSD пытается сделать его таковым.

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

В FEOD common это как перевалочный пункт для сущностей которые еще не сформировались в модуль. И как только придет момент они могут быть перемещены в модуль и удалены из common. В то время как в FSD shared это слой в первую очередь уровня абстракции, поэтому элементы из него могут быть перенесены в крайне редких случаях.

Связанные разделы

  • Modules — модули, использующие common-сущности
  • Global — глобальные сущности, доступные без импорта
  • Pages — страницы, использующие common компоненты