WARNING
Эта статья сгенерирована AI и требует ручной доработки.
FEOD для бэкенда и BFF
FEOD можно применять не только во фронтенде. Для BFF и серверных приложений подход полезен тем же самым: структура предсказуема, правила импорта понятны, масштабирование не превращает проект в хаос.
Ниже — минимальная, но достаточная модель уровней и правил для бэкенда.
Базовые уровни
- app: bootstrap приложения, интеграции, конфигурации, точка входа.
- routes: маршруты (часто file-based routing).
- middlewares: middleware уровня приложения.
- modules: бизнес-логика и доменные модули.
- global: сущности без импорта (типы, полифиллы, расширения интерфейсов).
Направление импорта
Главный принцип остаётся тем же: верхние уровни знают о нижних, но не наоборот.
app -> (routes | middlewares) -> modules
global не импортируетсяДополнительно:
routesиmiddlewaresне импортируют друг друга.- модули импортируются только через
index.ts. - внутренние файлы модуля не импортируются извне.
Пример структуры
src/
app/
entry.ts
integrations/
db.ts
routes/
users/
[id].ts
modules/
Users/
index.tsПоказана только загрузка пользователя. app/integrations/db.ts предоставляет настроенную функцию чтения из БД; серверный фреймворк передаёт обработчику id из URL.
// modules/Users/index.ts
export interface User {
id: string
name: string
email: string
}
export function createUsers(loadUser: (id: string) => Promise<User | null>) {
return { getById: loadUser }
}// routes/users/[id].ts
import type { createUsers } from '@/modules/Users'
export function createGetUser(users: ReturnType<typeof createUsers>) {
return async (id: string): Promise<Response> => {
const user = await users.getById(id)
if (!user) return Response.json({ error: 'Пользователь не найден' }, { status: 404 })
return Response.json({ id: user.id, displayName: user.name })
}
}// app/entry.ts
import { createUsers } from '@/modules/Users'
import { createGetUser } from '@/routes/users/[id]'
import { loadUser } from './integrations/db'
export const getUser = createGetUser(createUsers(loadUser))Обработчик преобразует доменный User в DTO без email. Чтение из БД остаётся зависимостью модуля; routes не импортирует инфраструктуру app. Аутентификация и обработка ошибок сервера здесь опущены. Это фрагмент адаптации запроса, а не готовый сервер.
Как это выглядит в BFF
BFF часто строится вокруг маршрутов и адаптации данных для фронтенда. FEOD помогает держать границы:
- routes: только адаптация запроса/ответа, маппинг DTO, валидация.
- modules: бизнес-логика, доступ к данным, доменные правила.
- middlewares: авторизация, трассировка, логирование.
- app: сборка всего вместе, подключение инфраструктуры.
Такой подход позволяет легко менять реализацию внутри модуля, не ломая маршруты.
Когда выделять подмодуль
Если модуль разрастается, используйте фрактальность:
modules/
Billing/
modules/
Invoices/
index.ts
Payments/
index.tsПодмодули имеют те же правила: собственный index.ts, изоляция, отсутствие глубоких импортов. Если маршрут выставляет счёт, он обращается к API Billing:
// modules/Billing/index.ts
export { createInvoice } from './modules/Invoices'// routes/invoices/create.ts
import { createInvoice } from '@/modules/Billing'Реализация createInvoice остаётся в Invoices; этот фрагмент показывает границу импорта, а не платёжный процесс.
Где держать типы
- Если типы используются внутри одного модуля — остаются в модуле.
- Если типы нужны без импорта по всему проекту — в
global. - Если типы универсальные, но с импортом — можно вынести в отдельный модуль (например,
SharedTypes).
Итог
FEOD для бэкенда сохраняет ключевые свойства методологии:
- чёткие уровни;
- ограничение связности;
- предсказуемая структура;
- возможность роста без пересмотра уровней.
Это особенно полезно в BFF, где важно быстро ориентироваться в проекте и не допускать сквозных зависимостей между маршрутизацией и доменной логикой.