Учебник веб-разработки
Разделы учебника
На этой странице

Начало

Архитектура и границы приложений

Оглавление

Для чего нужен каждый компонент

КомпонентЗадачаГраница
Next.jsСтраницы, tRPC, webhook, резервный media proxyТолько опубликованный контент через CMS API
MantineКомпоненты, тема и Styles APIДанные приходят через props
contracts/ZodТипы и проверка HTTP данныхTypeScript не заменяет runtime validation
StrapiРедактирование, Draft & Publish, REST и upload providerСобственные npm/React 18/TS 5
GarageS3 API и public website bucketОдин узел, без AWS ACL и без HA
PostgreSQLБаза StrapiОтдельный контейнер, тот же VPS
CaddyTLS, reverse proxyНе заменяет права CMS/API token

Картина на одном VPS

Исходник схемы
flowchart LR
  Visitor[Посетитель] --> Caddy
  Caddy --> Web[Next.js]
  Caddy --> CMS[Strapi]
  Caddy -->|media origin| S3[Garage]
  Web -->|read-only / tagged cache| CMS
  Web -->|legacy media proxy| S3
  CMS -->|S3 upload| S3
  CMS --> CMSDB[(PostgreSQL: strapi)]
  CMS -->|webhook| Web

База Strapi находится в PostgreSQL; все контейнеры — на одном VPS. Сбой базы, CMS или хоста отражается на обновлении контента. Готовые страницы кеширует Next.js. Read-only token не передаётся браузеру; панель защищена собственным входом Strapi, а публичные изображения читаются без пароля. Draft media-файлы не становятся приватными автоматически.

Структура монорепозитория

apps/
  web/             Next.js, CMS adapter и серверные тесты
  docs/            Статический сайт учебника
packages/
  contracts/       Zod-схемы и типы
  ui/              Независимый React/UIKit на Mantine и Storybook
infra/
  strapi/          Самостоятельный npm-проект CMS
  postgres/        Первичное создание пользователей и баз
  garage/          S3-совместимое объектное хранилище
  docs/            Образ и конфигурация учебника
  storybook/       Образ и конфигурация каталога компонентов
  Caddyfile*       Локальный и production reverse proxy
scripts/
  setup-env.mjs    Локальные секреты
  deploy/          Подготовка хоста и применение релизов
.sourcecraft/ci.yaml CI, образы и доставка
docs/              Это руководство

В pnpm workspace входят только apps/* и packages/*. infra/strapi использует свой package-lock.json, React 18 и TypeScript 5. Изоляция позволяет использовать современные версии в сайте, не заставляя Strapi разделять несовместимые зависимости.

Зависимости пакетов

Исходник схемы
flowchart TD
  Web[web: Next.js] --> Contracts[contracts: Zod]
  Web --> UI[ui: React и Mantine]
  UI --> Mantine[Mantine]
  Docs[docs: Next.js и MDX]
  Strapi[Strapi: отдельный npm lockfile]

Стрелки обозначают импорты: web использует contracts и ui. UIKit не импортирует Next.js или CMS; приложение передаёт данные и адаптер ссылок. Docs самостоятельно читает Markdown/MDX и не обращается к CMS. workspace:* означает локальный пакет, а не скачивание одноимённого пакета из npm. pnpm -r build учитывает зависимости workspace: сначала собирается contracts. Для сборки workspace отдельный оркестратор не нужен; команды выполняет pnpm.

Пути данных

Чтение: Strapi REST → серверный CMS client → Zod/contracts → repository → Server Component или tRPC → посетитель. Все страницы используют одну модель данных. Полная pagination читается до конца; ошибка второй страницы не выдаётся за полный список.

Обновление: Strapi webhook → HTTP handler → Zod → инвалидирование кеша Next.js. Страницы обновляются при следующем посещении без полной пересборки.

Медиа: браузер → Caddy → Garage website endpoint. CMS-адаптер нормализует адрес до публичного /filename; Caddy добавляет внутренний префикс strapi/. Next.js участвует в передаче только для старых local uploads через /api/media/.

Импорт: явный снимок публичного источника → нормализация HTML/дат → upload → Document Service create/publish. Архив нужен для аудита, не для runtime fallback.

Сборка, запуск и проверка типов

  • Сборка превращает TypeScript contracts в JavaScript, а Next.js — в production-приложение со статически сгенерированными страницами и ISR.
  • Запуск исполняет уже подготовленный код. В dev Next.js обрабатывает исходники.
  • Проверка типов ищет ошибки без выполнения бизнес-операций.
  • Runtime-валидация проверяет данные, поступившие от внешнего источника.

TypeScript не защищает автоматически от неверного HTTP JSON: сетевой клиент может не использовать TypeScript вообще. Поэтому webhook проверяется Zod в web.

Принятые решения

Next.js выбран для страниц и серверного API. Vike в репозитории не используется. Mantine выбран для интерфейса. Jest работает через next/jest, поэтому отдельный Vite-проект для тестирования не нужен. Vitest и Playwright не установлены.

Отдельное исключение — SPA ожидания холодного стенда в apps/standby: Vite собирает React/Mantine в один HTML с встроенными стилями и скриптом. Его обслуживает Python-диспетчер, пока контейнеры стенда остановлены. Он не заменяет Next.js или Storybook и не читает CMS-контент.

Strapi собирается из стандартного проекта своим Dockerfile. Мы не полагаемся на неопределённый сторонний «готовый Strapi image».

Где смотреть код

Workspace, root scripts, Dockerfile, Compose, контракты.

Наблюдаемость

Новый самостоятельный Compose-стек описан в infra/observability: браузер → Umami, ошибки Next.js → GlitchTip, Node/Blackbox Exporter → Prometheus → Grafana. UIKit не импортирует SDK аналитики или ошибок; интеграция находится в apps/web. Базы аналитики и ошибок отделены от Strapi ролями и владельцами.

Внешняя доставка алертов отключена; Telegram является учебным упражнением. Все системы наблюдения размещаются на том же VPS и разделяют его отказ. Пояснение DevOps · Практическая глава.