14 KiB
Onboarding для нового разработчика food-market
Этот документ — путь от «клонировал репо» до «открыл первый PR» за 3 дня. Если что-то не сходится с реальностью — это баг документации, отредактируй и оставь PR.
День 1 — установка и первый запуск
Что нужно
- macOS / Linux. Windows только для POS WPF (см. отдельно ниже).
- .NET 8 SDK (точная версия из
global.json—dotnet --list-sdksдолжен показывать её; если нет —winget install Microsoft.DotNet.SDK.8/brew install dotnet@8). - Node.js 20+ (
nvm install 20 && nvm use 20). - pnpm 9+ (
npm i -g pnpm). - PostgreSQL 14+ (на macOS:
brew install postgresql@14 && brew services start postgresql@14). - git, curl, python3 (для скриптов в
tests/e2e/). - Docker для интеграционных тестов (Testcontainers) и stage-deploy.
Установка проекта
git clone http://192.168.1.193:3000/nns/food-market.git
cd food-market
# БД для dev — пустая, инициируется миграциями автоматически.
createdb -U $USER food_market
# Backend
dotnet restore
dotnet build food-market.sln -c Debug --nologo
# Web frontend
cd src/food-market.web && pnpm install && cd ../..
Запуск
# Терминал 1: API
ASPNETCORE_ENVIRONMENT=Development dotnet run --project src/food-market.api
# → http://localhost:5081, Swagger на /swagger
# Терминал 2: Web SPA
cd src/food-market.web && pnpm dev
# → http://localhost:5173
Проверка что работает
# Health
curl http://localhost:5081/health/ready
# Зарегистрируйся
curl -X POST http://localhost:5081/api/auth/signup \
-H "Content-Type: application/json" \
-d '{"organizationName":"DevOrg","email":"dev@local.test","password":"DevPass123!","phone":"+77001234567"}'
# Логин и получи токен
TOKEN=$(curl -sX POST http://localhost:5081/connect/token \
-d 'grant_type=password&username=dev@local.test&password=DevPass123!&client_id=food-market-web&scope=openid profile email roles api offline_access' \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')
# Что я могу
curl -sH "Authorization: Bearer $TOKEN" http://localhost:5081/api/me | python3 -m json.tool
Тесты
# Unit
dotnet test tests/food-market.UnitTests --nologo
# Integration (нужен Docker — Testcontainers поднимает Postgres-контейнер)
dotnet test tests/food-market.IntegrationTests --nologo
# E2E (Playwright против локального API)
cd tests/e2e
E2E_ADMIN_URL=http://localhost:5081 ./run.sh stage-smoke
День 2 — где что лежит
Структура (укрупнённо)
food-market/
├── src/
│ ├── food-market.domain/ — POCO + enum'ы + интерфейсы. Без EF, без ASP.NET.
│ ├── food-market.application/ — DTO, FluentValidation, MediatR-handler'ы, Mapster config.
│ ├── food-market.infrastructure/ — EF Core, миграции, Identity, OpenIddict storage.
│ ├── food-market.api/ — Controllers, middleware, Hangfire jobs, OpenIddict server.
│ ├── food-market.web/ — React 19 + Vite SPA админки (admin.food-market.kz).
│ ├── food-market.public/ — Astro marketing-сайт (food-market.kz).
│ ├── food-market.shared/ — DTO-контракты сервер↔POS.
│ ├── food-market.pos.core/ — POS-логика (UI-agnostic).
│ └── food-market.pos/ — WPF (net8.0-windows; собирается на любой OS).
├── tests/
│ ├── food-market.UnitTests/ — xUnit + InMemory EF (быстрые юниты).
│ ├── food-market.IntegrationTests/— xUnit + Testcontainers Postgres (через WebApplicationFactory).
│ ├── e2e/ — Playwright (TS) + ad-hoc Python smoke-скрипты.
│ └── load/ — k6 (нагрузочные).
├── deploy/ — Dockerfile.{api,web,public}, compose, nginx, скрипты (prod-deploy/rollback/smoke/anonymize/swagger-diff).
├── docs/ — markdown (этот файл — `ONBOARDING.md`, плюс ARCHITECTURE/RUNBOOK/etc).
└── food-market.sln
Что почитать в первую очередь
Порядок имеет значение — от general к specific:
- ARCHITECTURE.md — общая картина: слои, deployment-топология, ключевые потоки, что реализовано / scaffolding / не реализовано (после 22 спринтов).
- glossary.md — все доменные термины (Tenant / Organization / Stock / RetailSale / …) с ссылками на классы.
- MULTI-TENANCY.md — query-filter, SuperAdmin override, как не утечь cross-org.
- DEVELOPER-GUIDE.md — паттерны (CQRS-like, MediatR, валидаторы, Mapster), стиль кода, как добавлять новые endpoint'ы.
- api-reference.md — auto-generated список всех 190+ endpoint'ов (обновляется weekly через Hangfire).
- error-codes.md — каталог HTTP-кодов для humanizeError на фронте.
- secrets.md — env-vars + где хранятся секреты.
- observability.md — Prometheus метрики, Serilog, /health.
- RUNBOOK.md — как разруливать инциденты («api не стартует», «остатки разъехались», и т.п.).
- performance-baseline.md — k6 baseline + что НЕ масштабируется.
Sprint-history
Хронология фич: docs/sprint1-progress.md … docs/sprint28-progress.md.
Каждый — что было сделано + цифры. Полезно когда видишь странное имя
файла и хочешь понять «когда и зачем».
После Sprint 24 — серия "quality marathon":
- 25 — hourly quality-watchdog (
~/quality-watchdog.sh) с auto-incident loop в Server-Claude очередь. - 26 — flaky-test detection (
tests/regression/find-flaky.sh) + observability stack (Grafana JSON + Prometheus alerts + RUNBOOK action-per-alert). - 27 — cross-feature integration tests (
tests/integration/) + 4-часовой soak (k6) + crash recovery (11.7s ≤ 30s SLA). - 28 — overnight maintenance: api-reference auto-gen fix (195→240 endpoints), HSTS header на stage, integration spec gap-fill (1C-CSV import, GDPR export, security headers).
Тестовый стенд
- Stage:
https://test.admin.food-market.kz—~/deploy-stage.shсобирает образ и катит. Подробности в stage-access.md. - Prod:
https://admin.food-market.kz— НЕ деплоится автоматически (Sprint 21 toolchain готов, см.deploy/prod-deploy.sh, но реальный сервер не настроен).
Git workflow
- Origin — Forgejo на
http://192.168.1.193:3000/nns/food-market.git. GitHub — mirror. - Никаких force-push'ей в main (после первого тэга).
- Branch для серьёзных фич:
feat/<sprint>-<short-name>, в Pull Request → Squash & Merge. - Каждый коммит на собственной фиче —
feat(scope): subject(см.git log --onelineдля примеров).
День 3 — первый PR
Найти первую задачу
grep -rn "TODO\|FIXME" src/— около 30 живых TODO. Самые маленькие обычно UX-полировка (i18n, copy, validation message).docs/sprintNN-progress.mdпоследнего спринта → раздел «Открытые TODO».- В Forgejo Issues (если есть): bug-001..004 в
tests/e2e/reports/bugs/— некоторые фиксы уже сделаны, остаются follow-up'ы. - Слабый шаг: посмотри
docs/performance-baseline.mdраздел «Сводка: что нужно поправить» — там список задач со статусом ✅/⚠️/❌.
Что сделать перед PR
git fetch origin && git rebase origin/main(memory:feedback_serialize_edits— мы один-коммитящий-за-раз; не делай параллельных правок).dotnet build+dotnet test(unit + integration) — должны быть зелёные.pnpm -C src/food-market.web exec tsc --noEmit— TS clean.- Локальный smoke если правил controller'ы: запусти API +
curlна затронутый endpoint. - Для UI: открой
/loginлокально, проверь что страница работает.
Шаблон PR-сообщения
<тип>(scope): краткое описание
Что: …
Зачем: …
Как тестировал: …
Связанные issue/sprint: …
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> (если работал в паре с Claude)
Кодстайл
- C#: дефолтный .NET-стиль. Один файл — один класс. Async/await везде
где I/O. EF-проекции через
.ProjectToType<TDto>(MapsterConfig.Config)для новых endpoint'ов (Sprint 20+). - TS: prettier+eslint конфиг в
src/food-market.web. Hooks naminguseFoo. Server-state — TanStack Query, не useState. - CSS: только Tailwind utility-classes. Никаких inline styles.
- Комментарии: только если объясняют почему, не что. Если
переписал паттерн — оставь reference на
[memory:feedback_serialize_edits]или sprint-doc.
FAQ
Q: API не стартует, ругается на global.json
dotnet --list-sdks должен содержать ровно ту версию что в global.json
(8.0.417). Если нет — установи SDK. НЕ редактируй global.json — это
сломает CI и другие dev-машины.
Q: Integration-тесты падают с "Cannot find docker daemon"
Включи Docker Desktop / sudo systemctl start docker. Testcontainers
тащит postgres:16-alpine (один раз, потом из кеша).
Q: Web стартует но не видит API
Проверь что src/food-market.web/vite.config.ts proxy указывает на
http://localhost:5081. Если порт API изменился — обнови.
Q: Сертификат OpenIddict не создаётся
Dev-режим: App_Data/ должен быть writable. Прод: см.
openiddict-keys.md.
Q: Как добавить новую сущность
Шаги (псевдо-flow):
- POCO в
src/food-market.domain/<Area>/MyEntity.cs(отTenantEntityесли связана с org'ой). - DbSet + EntityTypeConfiguration в
src/food-market.infrastructure/Persistence/AppDbContext.cs+Configurations/. - Migration в
Migrations/<timestamp>_<name>.cs— ВРУЧНУЮ, не черезdotnet ef migrations add. Обязательны[Migration("id")]+[DbContext(typeof(AppDbContext))](memory:feedback_ef_migrations). - DTO + Validator в
src/food-market.application/<Area>/. - Mapster TypeAdapterConfig в
MapsterConfig.Build()если есть нетривиальное проецирование. - Controller в
src/food-market.api/Controllers/<Area>/. Atomic per endpoint, multi-tenant через query-filter (автоматически). - Integration-тест в
tests/food-market.IntegrationTests/<Area>Tests.cs— минимум один happy-path. - Если возвращаешь в Web — обновить
src/food-market.web/src/lib/types.ts.
Q: Как запустить нагрузочный тест
cd tests/load
BASE_URL=http://localhost:5081 k6 run retail-sales-parallel.js
# или против stage:
BASE_URL=https://test.admin.food-market.kz k6 run signup-burst.js
См. performance-baseline.md для интерпретации цифр.
Q: Где POS WPF тестировать
Нужен Windows. На macOS/Linux можно собрать (dotnet build src/food-market.pos)
но не запустить UI. Тесты POS-логики в src/food-market.pos.core —
кроссплатформенные.
Q: Хочу понять как работает …
- Tenant isolation →
MULTI-TENANCY.md+AppDbContext.ApplyTenantFilter. - OpenIddict →
openiddict-keys.md+ Program.csAddOpenIddict(). - POS sync с idempotency →
food-market.pos.core+PosBatchAckController. - ОФД →
ofd-integration.md+Infrastructure/Fiscal/. - CSV import →
imports.md+ProductsController.ImportCsv. - GDPR org export →
OrgExportJob(Sprint 22).
Где спрашивать
- Forgejo issues — для багов и feature requests.
- В коде — поиск по docstring (комментарии часто отвечают «почему сделано именно так»).
- Sprint-progress файлы — там цифры и trade-off'ы зафиксированы.
- Memory-файлы Claude Code в
~/.claude/projects/-home-nns-food-market/memory/— что-то типа CHANGELOG развития, более информально.
Welcome! 🚀