mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 15:30:16 +03:00
103 lines
6.2 KiB
Markdown
103 lines
6.2 KiB
Markdown
|
|
# Demo frontend
|
|||
|
|
|
|||
|
|
Полноценное Next.js 16 приложение поверх меньшего из двух fixture API: `../demo-backend/openapi/simple.json`.
|
|||
|
|
|
|||
|
|
Пример показывает SLM Level 1 на каталоге: public data, JWT login/refresh/logout, persisted cart, checkout, protected orders, RBAC, admin CRUD, optimistic locking и детерминированные network outcomes.
|
|||
|
|
|
|||
|
|
## Запуск
|
|||
|
|
|
|||
|
|
Требуются Node.js 20+, npm 10+ и современный browser с Web Locks API. Auth и cart transitions работают fail-closed без cross-tab lock; `localhost` считается secure context для локальной разработки.
|
|||
|
|
|
|||
|
|
Сначала запустите Simple API:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd ../demo-backend
|
|||
|
|
npm install
|
|||
|
|
npm run dev:simple
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Затем запустите frontend:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm install
|
|||
|
|
npm run dev
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Откройте `http://localhost:3000`. Backend по умолчанию доступен на `http://localhost:3001`.
|
|||
|
|
|
|||
|
|
Чтобы изменить URL backend, создайте `.env.local` рядом с `package.json`:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
NEXT_PUBLIC_SIMPLE_API_URL=http://localhost:3001
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Demo accounts
|
|||
|
|
|
|||
|
|
| Email | Password | Role | Scenarios |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| `admin@demo.local` | `demo1234` | admin | Product CRUD, all orders |
|
|||
|
|
| `customer@demo.local` | `demo1234` | customer | Cart, checkout, own orders |
|
|||
|
|
|
|||
|
|
## Routes
|
|||
|
|
|
|||
|
|
| Route | Composition responsibility |
|
|||
|
|
|---|---|
|
|||
|
|
| `/` | Product search, category filter, sorting and pagination |
|
|||
|
|
| `/products/[productId]` | Product detail and add-to-cart |
|
|||
|
|
| `/cart` | Multi-domain checkout coordination |
|
|||
|
|
| `/orders` | Protected order history and cancellation |
|
|||
|
|
| `/sign-in` | JWT session lifecycle and account selection |
|
|||
|
|
| `/admin/products` | RBAC-gated CRUD and optimistic locking |
|
|||
|
|
|
|||
|
|
Плавающий `Demo controls` widget переключает `X-Demo-Scenario`: slow, timeout, 500, 429, empty, expired auth, forbidden и conflict. Отдельное действие `Seed 250` загружает настоящий большой seed с интерактивными product IDs; synthetic `large-dataset` header намеренно не используется для mutation UI.
|
|||
|
|
|
|||
|
|
## SLM boundary
|
|||
|
|
|
|||
|
|
`src` является SLM root:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
src/
|
|||
|
|
├── app/ Next.js bootstrap and route entries
|
|||
|
|
├── compositions/ Layout, screens and demo widget
|
|||
|
|
├── domains/ Auth, catalog, cart, orders and demo-control
|
|||
|
|
├── infra/ Generated REST client, JWT storage and browser storage
|
|||
|
|
├── ui/ Product-agnostic button, form field and feedback panel
|
|||
|
|
└── shared/ Deterministic predicates, formatting and Result type
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Пример использует Level 1 осознанно: у каждого домена одна browser runtime integration. Дополнительные factories, adapters и assemblies Level 2 не окупили бы стоимость. Checkout остаётся в composition, поэтому `orders` не импортирует `cart`, а module graph остаётся ацикличным.
|
|||
|
|
|
|||
|
|
State и lifecycle распределены по владельцам:
|
|||
|
|
|
|||
|
|
- `auth` владеет пользовательской сессией; `infra/simple-auth-session` хранит technical JWT pair, стабильный `sessionId` и CAS revision.
|
|||
|
|
- `cart` владеет строками, totals и persisted snapshot validation; monotonic revision и conditional clear сериализованы между вкладками.
|
|||
|
|
- SWR cache принадлежит REST infra module, создаётся отдельным provider и remount-ится при смене logical auth session.
|
|||
|
|
- React providers создаются один раз в application scope и очищают свои subscriptions при unmount.
|
|||
|
|
- Route entries только адаптируют Next.js params и подключают public APIs compositions.
|
|||
|
|
- Reset, seed, role и request-scenario transitions синхронизируют cache, auth, cart и route-local pagination между вкладками.
|
|||
|
|
|
|||
|
|
## REST client
|
|||
|
|
|
|||
|
|
Split SDK генерируется из committed OpenAPI:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm run codegen:simple-rest-api
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Generated-код живёт только в `src/infra/simple-rest-api/generated` и не редактируется вручную. GET hooks вызывают точечные operations через `simpleHttpClient`; submit-сценарии используют полный `simpleRestApi`. Внешний код импортирует REST capability только через `@/infra/simple-rest-api`.
|
|||
|
|
|
|||
|
|
OpenAPI fixture описывает числовые `page` и `limit` как `object`. Исправление generated type изолировано в `types/to-generated-query.ts`; runtime query остаётся числовым.
|
|||
|
|
|
|||
|
|
JWT refresh выполняется в transport `onError`, ограничен одним retry и дедуплицирует конкурентные refresh requests одной revision. CAS не позволяет позднему refresh воскресить logout или перезаписать новый login. Хранение refresh token в `localStorage` допустимо только для этой архитектурной fixture; production-приложение должно выбрать threat model и более безопасную session strategy.
|
|||
|
|
|
|||
|
|
Checkout привязан к captured auth session и persisted cart revision. Backend атомарно проверяет product version, unit price, USD currency, aggregate stock и уникальность product lines. Timeout-сценарий откладывает mutation handler и отменяет его при disconnect, поэтому frontend timeout не скрывает завершившийся POST.
|
|||
|
|
|
|||
|
|
## Проверка
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm run check
|
|||
|
|
npm run build
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`npm run check` запускает architecture constraints, ESLint, TypeScript и Vitest. Architecture script строит import graph через TypeScript AST и явный module manifest, затем проверяет направление слоёв, public module imports, side-effect/dynamic imports, запрет утечки generated SDK и циклы.
|