Skip to main content

Архитектура backend API


Архитектура backend API (@lowcode/api)

Как устроен сервер приложений: модули NestJS, слои, потоки запросов и работа с БД.

🎯 Задача страницы

Эта страница описывает архитектуру приложения @lowcode/api:

  • как организованы модули и слои NestJS;
  • как устроен жизненный цикл HTTP-запроса;
  • как API работает с БД через Prisma;
  • как реализованы основные сценарии: проекты, версии, компиляция DSL;
  • где искать код и как добавлять новые функции.

1. Роль @lowcode/api в общей архитектуре

@lowcode/api — центральный backend-сервис платформы. Он:

  • хранит проекты и их версии в PostgreSQL;
  • принимает и валидирует DSL-структуры;
  • вызывает dsl-compiler для генерации бандла;
  • отдаёт данные редактору builder-web и runtime-хосту;
  • предоставляет REST API для всех ключевых операций.

Высокоуровневая схема:

graph TD
BW[builder-web] -->|HTTP| API[@lowcode/api]
API --> DB[(PostgreSQL)]
API --> DSL[dsl-compiler]
DSL --> BNDL[JS Bundle]
BNDL --> RH[runtime-host]

2. Структура приложения NestJS

Архитектурно @lowcode/api использует стандартный подход NestJS:

  • модули (modules) — логические блоки функциональности;
  • контроллеры (controllers) — HTTP-эндпоинты;
  • сервисы (services) — бизнес-логика;
  • инфраструктурный слой — Prisma, конфигурация, фильтры, пайпы.

Типичная структура каталога:

apps/api/src/
main.ts ← точка входа (NestFactory + FastifyAdapter)
app.module.ts ← корневой модуль

projects/ ← работа с проектами
projects.module.ts
projects.controller.ts
projects.service.ts
dto/

versions/ ← работа с версиями проектов
dsl/ ← валидация и операции с DSL
compile/ ← компиляция DSL → bundle

database/ ← PrismaService, PrismaModule
common/ ← общие фильтры, пайпы, декораторы

Для деталей реализации см. также:


3. Жизненный цикл HTTP-запроса

Упрощённый путь любого запроса:

Fastify → NestJS → Controller → Service → Prisma/DSL → Response

3.1. Fastify + NestFactory

В main.ts создаётся приложение NestJS на основе Fastify:

const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter());

Также на этом этапе:

  • настраиваются глобальные пайпы (ValidationPipe);
  • включается CORS (для доступа builder-web);
  • конфигурируются префиксы роутов и пр.

См. подробности в AppModule.

3.2. Контроллер

Контроллер отвечает за приём HTTP-запросов:

@Controller('projects')
export class ProjectsController {
constructor(private readonly service: ProjectsService) {}

@Get()
findAll() {
return this.service.findAll();
}

@Post()
create(@Body() dto: CreateProjectDto) {
return this.service.create(dto);
}
}

Особенности:

  • маршруты объявляются через декораторы @Get, @Post, @Patch и т.д.;
  • входные данные типизируются DTO-классами и валидируются пайпами.

Реализация контроллера проектов описана в ProjectsController.

3.3. Сервис

Сервис инкапсулирует бизнес-логику и работу с БД:

@Injectable()
export class ProjectsService {
constructor(private readonly prisma: PrismaService) {}

findAll() {
return this.prisma.project.findMany();
}

create(dto: CreateProjectDto) {
return this.prisma.project.create({ data: dto });
}
}

Сервисы не знают о деталях HTTP — только о доменной логике.

Подробное описание сервиса проектов см. в ProjectsService. Инфраструктурный слой доступа к БД реализован в PrismaService.

3.4. Ответ

NestJS автоматически сериализует возвращаемые объекты в JSON и отправляет их клиенту (builder-web или runtime-host).


4. Работа с БД: Prisma + PostgreSQL

4.1. Prisma schema

Модели данных находятся в apps/api/prisma/schema.prisma. Там описаны сущности:

  • Project — проект пользователя;
  • ProjectVersion — версии проекта;
  • дополнительные сущности (шаблоны, пользователи и т.п. при расширении).

Пример условной модели:

model Project {
id String @id @default(cuid())
name String
createdAt DateTime @default(now())
versions ProjectVersion[]
}

4.2. PrismaService и PrismaModule

Для интеграции Prisma с NestJS используется обёртка PrismaService:

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
}

Она регистрируется в PrismaModule и импортируется в AppModule.

Сервисы получают PrismaService через DI и используют его для запросов:

this.prisma.project.findMany();

4.3. Миграции и окружения

  • миграции лежат в apps/api/prisma/migrations/*;
  • в Docker-окружениях они применяются автоматически при старте API;
  • локально миграции выполняются через скрипты pnpm prisma:migrate-dev / deploy.

5. Доменные модули: проекты, версии, DSL, компиляция

5.1. Модуль Projects

Отвечает за:

  • создание, обновление, удаление проектов;
  • получение списка и деталей проекта.

Типичные эндпоинты:

GET    /projects
GET /projects/:id
POST /projects
PATCH /projects/:id
DELETE /projects/:id

Реализация доменной логики и HTTP-слоя для проектов:

5.2. Модуль Versions

Модуль версий (ProjectVersion):

  • хранит снимки состояния проекта во времени;
  • позволяет откатываться к предыдущим версиям;
  • обеспечивает совместимость с компилятором.

Примерный набор эндпоинтов:

GET    /projects/:id/versions
GET /project-versions/:versionId
POST /projects/:id/versions

Реализация версий проекта:

5.3. Модуль DSL

Роль:

  • валидация DSL-дерева, которое приходит от builder-web;
  • базовые проверки структуры, типов компонентов, связей.

API-слой может предоставлять эндпоинты вида:

POST /dsl/validate

При вызове:

  • принимается JSON-описание проекта;
  • запускаются валидаторы из dsl-compiler;
  • возвращается результат валидации (ошибки, предупреждения).

Ключевые элементы модуля DSL:

Для низкоуровневой трансляции DSL в AST и дальше в React/HTML см. документацию к пакету dsl-compiler и его индексным функциям, например compileDslToReact и buildAstFromDsl.

5.4. Модуль Compile

Роль:

  • принимать DSL-дерево и проходить весь цикл DSL → AST → Bundle;
  • возвращать готовый JS-бандл редактору или runtime-host.

Эндпоинт, например:

POST /compile/react
POST /compile/html

Внутри сервис компиляции использует DTO для разных целевых форматов и вызывает функции dsl-compiler:

Сервис и контроллер компиляции входят в модуль DslModule и разделяют инфраструктуру с валидацией.


6. Общие компоненты: DTO, валидация, ошибки

6.1. DTO и ValidationPipe

DTO-классы используют декораторы class-validator:

export class CreateProjectDto {
@IsString()
name: string;
}

Типичный пример DTO для проектов — CreateProjectDto и UpdateProjectDto.

Глобальный ValidationPipe в main.ts гарантирует, что:

  • входные данные соответствуют типам;
  • лишние поля отбрасываются (whitelist: true).

6.2. Фильтры ошибок

Через NestJS ExceptionFilter можно:

  • унифицировать формат ошибок;
  • логировать непойманные исключения;
  • возвращать понятные ответы клиенту.

Пример: глобальный фильтр HTTP-ошибок, оформляющий ошибки в стандартный JSON-формат.


7. Тестирование API

@lowcode/api покрывается тестами на нескольких уровнях:

  • unit-тесты сервисов (мокаем PrismaService);
  • integration-тесты модулей (через Test.createTestingModule);
  • e2e-тесты в Docker-окружении (API тестируется через HTTP как чёрный ящик).

Тесты лежат в:

apps/api/src/**/*.spec.ts

Подробнее система тестирования описана на странице Frameworks → Jest.


8. Где искать ключевые части кода

КомпонентПутьОписание
Точка входаapps/api/src/main.tsNestFactory, FastifyAdapter, глобальные пайпы
Корневой модульapps/api/src/app.module.tsПодключение доменных модулей и инфраструктуры
Модуль Projectsapps/api/src/projects/*CRUD операций над проектами
Модуль Versionsapps/api/src/versions/*Управление версиями проектов
Модуль DSL/Compileapps/api/src/dsl/*, apps/api/src/compile/*Валидация и компиляция DSL
Prisma schemaapps/api/prisma/schema.prismaМодели БД
Prisma migrationsapps/api/prisma/migrations/*Миграции схемы БД
PrismaService/Moduleapps/api/src/database/*Интеграция Prisma с NestJS

Документацию по основным классам и модулям см. в разделе reference:


9. Как расширять API безопасно

При добавлении новой функциональности рекомендуется придерживаться паттерна:

  1. Создать модуль: feature/feature.module.ts.
  2. Создать сервис с бизнес-логикой.
  3. Создать контроллер с HTTP-эндпоинтами.
  4. Описать DTO и включить их в валидацию.
  5. При необходимости добавить модели в schema.prisma и миграции.
  6. Написать unit-/integration-тесты.
  7. Зарегистрировать новый модуль в AppModule.

Так архитектура остаётся модульной, предсказуемой и масштабируемой.