WARNING
Эта статья сгенерирована AI и требует ручной доработки.
Паттерны FEOD
В этой статье собраны практические паттерны, которые опираются на правила FEOD и помогают держать модули изолированными, а структуру — предсказуемой.
1. Публичный API модуля
Идея: любой доступ к модулю идёт через index.ts. Это защищает внутренности и упрощает рефакторинг.
modules/
Billing/
index.ts
api/
composables/
types/// modules/Billing/index.ts
export { createInvoice } from './api/createInvoice'
export { useBilling } from './composables/useBilling'
export type { Invoice } from './types'2. Фрактальный разрез
Идея: когда модуль становится слишком большим, выделяйте подмодуль без смены правил.
modules/
Billing/
modules/
Invoices/
Payments/Подмодуль сам изолирован и экспортирует свой API через index.ts.
3. Композиция на уровне Pages
Идея: страницы не содержат бизнес-логики, они собирают модули в сценарий.
pages/
dashboard.vue<!-- pages/dashboard.vue -->
<script setup lang="ts">
import { UserSummary } from '@/modules/UserSummary'
import { BillingOverview } from '@/modules/Billing'
</script>
<template>
<main>
<UserSummary />
<BillingOverview />
</main>
</template>Каждый модуль экспортирует свой компонент через index.ts. Страница определяет их расположение, а загрузка данных остаётся внутри модулей.
4. Интеграции живут в App
Идея: внешние сервисы подключаются в app, а модули остаются независимыми.
app/
entry.ts
integrations/
analytics.ts
modules/
Analytics/
index.ts// modules/Analytics/index.ts
export interface AnalyticsClient {
track(event: string): void
}
export function createAnalytics(client: AnalyticsClient) {
return { pageOpened: () => client.track('page_opened') }
}// app/integrations/analytics.ts
import { createAnalytics } from '@/modules/Analytics'
export const analytics = createAnalytics({
track(event) {
void fetch('/api/events', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event }),
keepalive: true
}).catch(() => { /* Телеметрия не блокирует страницу. */ })
}
})
analytics.pageOpened()В app/entry.ts импортируется analytics из ./integrations/analytics; импорт выполняет показанную настройку и отправку события.
5. Вынос в Common только при универсальности
Идея: в common попадают только универсальные, одиночные сущности. Если требуется несколько файлов или появляется бизнес-смысл, это уже modules.
common/
utilities/
formatDate.ts
modules/
Orders/
utilities/
calculateTotals.ts6. Приватный модуль страницы
Идея: если странице нужна локальная логика, используйте приватный модуль с префиксом _.
pages/
orders/
index.vue
_orders/
index.ts
components/
OrderList.vue// pages/orders/_orders/index.ts
export { default as OrderList } from './components/OrderList.vue'<!-- pages/orders/index.vue -->
<script setup lang="ts">
import { OrderList } from './_orders'
</script>
<template><OrderList /></template>Приватный модуль не используется вне своей страницы.
7. Зигзагообразные импорты запрещены
Идея: подмодуль не должен импортировать внешний уровень своего модуля или соседний подмодуль другого модуля.
modules/
A/
modules/
A1/
B/
modules/
B1/A1 не импортирует B1 напрямую — только публичный API B.
// modules/A/modules/A1/load.ts
import { loadBilling } from '@/modules/B' // допустимо: публичный API B// modules/A/modules/A1/load.ts
import { loadPayments } from '@/modules/B/modules/B1' // запрещено: обход API B