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
Границу нельзя вычислить
Однозначного правила «common или модуль» нет, как нет его для «модуль или подмодуль». Практические ориентиры: сколько связей у сущности с другими и есть ли в ней бизнес-логика. Если связей много или логика бизнесовая, это кандидат в модуль. Если сущность без бизнес-логики и почти ни с чем не связана, она может жить в common. Решение остаётся за командой.
Важные правила
- В отдельных случаях возможно использование двух и более файлов на одну сущность, но рекомендуется этого избегать и предпочитать в этом случае Module
- Common могут взаимно использовать друг друга, пока это не создаёт слишком жёсткие связи
- Сущности в Common не могут использовать индексные файлы
Почему common не импортирует modules
Главный довод: common образует набор листьев графа зависимостей. Пока он ничего не импортирует из modules, цикл с его участием невозможен, и проверять циклы нужно только среди модулей. Поэтому исключения из этого правила (см. «UI-модуль и Common» ниже) стоит делать осознанно: каждое из них ослабляет эту гарантию.
Почему запрещено использовать индексные файлы?
- Это создаёт ненужные файлы, которые не несут в себе смысловой нагрузки
- Меньше ненужных действий для создания сущности (не нужно создавать индексный файл и добавлять в него все импорты + следить за этим)
- Это мотивирует при необходимости использовать модули
- Это косвенно вынуждает держать уровень common тонким и не засорять его
Структура
Зачёркнутые index.ts показывают запрещённые barrel-файлы. Сущности common импортируются напрямую из своих файлов.
Примеры сущностей Common
Единый пример собирает UI, композаблы, утилиты и типы в одном дереве и наборе файлов.
Выберите файл в дереве слева
Общий тип не знает, какой модуль получает ответ. Потребитель задаёт тип данных:
// modules/UserManagement/types/usersResponse.ts
import type { ApiResponse } from '@/common/types/api.types'
import type { User } from './user.types'
export type UsersResponse = ApiResponse<User[]>Использование Common в проекте
Правильный импорт
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:
// common/composables/useNotification.ts
export interface NotificationService {
show(message: string): void
}
export function useNotification(service: NotificationService) {
return { notify: (message: string) => service.show(message) }
}Реализацию выбирает app; вызов через объект сохраняет this сервиса.
// modules/Notifications/index.ts
export class Notifications {
constructor(private container: HTMLElement) {}
show(message: string): void {
this.container.textContent = message
}
}// app/integrations/notifications.ts
import { useNotification } from '@/common/composables/useNotification'
import { Notifications } from '@/modules/Notifications'
const container = document.querySelector<HTMLElement>('#notifications')
if (!container) throw new Error('Нет контейнера уведомлений')
export const { notify } = useNotification(new Notifications(container))
notify('Изменения сохранены')Контейнер #notifications находится в HTML приложения. common зависит только от интерфейса; app выбирает реализацию из Notifications.
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 не нужен публичный API (индексный файл), а для модуля он есть всегда. Если сущность зависит от модулей, ей место среди модулей, даже если это один файл.
Common как модуль
Может возникнуть вопрос, почему Common не является модулем сам по себе?
- Common не является модулем, потому что он не предназначен для взаимодействия с другими модулями.
- Common предназначен для хранения общих сущностей, которые могут быть использованы в любом месте проекта без особой специфики.
- Для common действуют особые правила экспорта, противоречащие правилам модулей (запрет на использование индексных файлов).
- Это позволяет поддерживать количество модулей минимально необходимым для проекта.
- Дополнительные правила в common служат противовесом для загрязнения общего уровня.
Однако существуют модификации FEOD, которые позволяют common быть модулем. Подробнее об этом вы можете прочитать в разделе Common Module.
Design System — это Common?
- Design System не вполне укладывается в common-уровень.
- Однако из-за сильной связанности элементов между собой, лучше держать его в отдельном модуле, а common оставить для примитивных сущностей.
- Однако если это просто набор базовых компонентов для отображения, не образующих дизайн-систему, то это может быть Common.
- Если вы используете стороннюю библиотеку компонентов, то вы вполне можете держать здесь переопределения компонентов и прочие донастройки.
Отличия common из FEOD и shared из FSD
В графе зависимостей common из FEOD и shared из FSD стоят на одном месте: внизу, ничего не импортируя из других слоёв, и могут взаимно использовать друг друга, пока это не создаёт слишком жёстких связей. Роль у них разная.
sharedв FSD — постоянная база, из которой строятся остальные слои. В версии 2.1 он тоже наполняется извлечением из страниц и виджетов, но то, что туда попало, обычно остаётся там.commonв FEOD — перевалочный пункт для сущностей, которые ещё не оформились в модуль. Когда они оформляются, они уходят в модуль.
Отсюда разница в правилах. В common индексные файлы не рекомендуются (в shared FSD они стандартны), глубина ограничена, а связанные сущности (UI-кит, дизайн-система) чаще живут в отдельном модуле. common не пытается быть слоем абстракции, из которого строятся остальные.
Типичный образ мышления в FSD: чтобы связать сущности верхних слоёв, опустить общее в нижний слой. В FEOD связанное лучше держать рядом по смыслу, в одном модуле или подмодуле, а не отрывать друг от друга по уровню абстракции.