REST API · Worker · PostgreSQL · Redpanda/Kafka · SignalR · React + Vite · OpenTelemetry · Prometheus · Grafana · Loki · VictoriaLogs · Jaeger
Order Tracking демонстрирует промышленный сценарий обработки заказов: API принимает команды, сохраняет данные в PostgreSQL, фиксирует интеграционные события через outbox, worker публикует события в Kafka, а API доставляет изменения клиентам через SignalR. Вся цепочка покрыта метриками, логами и трассировками.
| Раздел | Что внутри |
|---|---|
| Назначение | какую задачу решает проект |
| Архитектура | слои, поток данных и основные компоненты |
| Технологический стек | backend, frontend, инфраструктура |
| Быстрый запуск | запуск через Docker Compose |
| Порты и сервисы | адреса API, Grafana, Prometheus, Jaeger и других сервисов |
| Рабочий сценарий | создание заказа, смена статуса, outbox, Kafka, SignalR |
| API и SignalR | REST-эндпоинты и realtime-события |
| Наблюдаемость | метрики, логи, трассировки и скриншоты |
| Тестирование и CI | локальные проверки и GitHub Actions |
| Документация | дополнительные markdown-файлы проекта |
Проект моделирует систему отслеживания заказов. Пользователь или внешний клиент может создать заказ, получить список заказов, открыть карточку заказа и изменить его статус. Внутри системы каждое важное изменение проходит через надежную событийную цепочку:
- API валидирует запрос и сохраняет изменения в PostgreSQL.
- Вместе с бизнес-операцией создается запись в outbox.
- Worker забирает необработанные outbox-сообщения и публикует их в Kafka.
- API получает событие из Kafka и рассылает обновление подключенным клиентам через SignalR.
- OpenTelemetry отправляет метрики, логи и трассировки в observability-стек.
Такой подход показывает, как строить сервис, который не теряет события при сбоях и при этом остается наблюдаемым: видно состояние API и worker, HTTP-нагрузку, работу БД, публикацию в Kafka, доставку SignalR и связь логов с trace/span.
Решение разделено по принципам Clean Architecture: доменная логика отделена от инфраструктуры, API и фонового worker.
| Слой | Назначение |
|---|---|
OrderTracking.Domain |
доменная модель, статусы заказов, правила переходов, базовые сущности |
OrderTracking.Contracts |
DTO и интеграционные события, которыми обмениваются компоненты |
OrderTracking.Application |
сценарии приложения, абстракции репозиториев и сервисов |
OrderTracking.Infrastructure |
EF Core, PostgreSQL, Kafka, outbox, реализации внешних зависимостей |
OrderTracking.Presentation.Api |
REST API, Swagger/OpenAPI, SignalR, метрики и трассировки API |
OrderTracking.Presentation.Worker |
фоновая обработка outbox и публикация событий в Kafka |
frontend |
клиентское приложение на React/Vite для работы с заказами |
flowchart LR
Client["Frontend / API client"] --> Api["OrderTracking API"]
Api --> Db[("PostgreSQL")]
Api --> Hub["SignalR Hub"]
Worker["Outbox Worker"] --> Db
Worker --> Kafka["Redpanda / Kafka"]
Kafka --> Api
Api --> Otel["OpenTelemetry Collector"]
Worker --> Otel
Otel --> Prometheus["Prometheus"]
Otel --> Loki["Loki"]
Otel --> VictoriaLogs["VictoriaLogs"]
Otel --> Jaeger["Jaeger"]
Prometheus --> Grafana["Grafana"]
Loki --> Grafana
VictoriaLogs --> Grafana
Ключевая идея проекта: бизнес-изменение и запись события происходят в одной транзакции БД, а публикация в Kafka выполняется отдельно. Благодаря этому событие не теряется, даже если broker или worker временно недоступны.
| Область | Технологии |
|---|---|
| Backend | .NET 9, ASP.NET Core, EF Core, NSwag, SignalR |
| Frontend | React 19, Vite, TypeScript, React Query, Axios, SignalR client |
| База данных | PostgreSQL 16 |
| Сообщения | Redpanda как Kafka-compatible broker |
| Observability | OpenTelemetry, Prometheus, Grafana, Loki, VictoriaLogs, Jaeger, OpenSearch |
| Инфраструктура | Docker Compose, GitHub Actions, CodeQL, Trivy, hadolint |
| Тесты | xUnit, unit-тесты домена, integration-тесты API |
Версии инструментов зафиксированы в global.json, Directory.Build.props, frontend/package.json и docker-compose.yml.
Для полного запуска достаточно Docker Compose:
docker compose up -dПосле старта поднимутся API, worker, frontend, PostgreSQL, Redpanda и весь стек наблюдаемости. Первичная инициализация Grafana, OpenSearch и контейнеров с .NET может занять 1-3 минуты.
Проверка API:
curl http://localhost:5086/healthОстановка:
docker compose downЕсли нужно удалить данные PostgreSQL и начать с чистого состояния:
docker compose down -vЕсли инфраструктура уже поднята в Docker, API и worker можно запускать из исходников:
docker compose up -d postgres redpanda jaeger otel-collector prometheus grafana loki victorialogscd src/OrderTracking.Presentation.Api
dotnet runcd src/OrderTracking.Presentation.Worker
dotnet run| Сервис | URL |
|---|---|
| Frontend | http://localhost:5173 |
| API health | http://localhost:5086/health |
| Swagger UI | http://localhost:5086/swagger |
| OpenAPI YAML | http://localhost:5086/api-docs/openapi.yaml |
| Prometheus | http://localhost:9090 |
| Grafana | http://localhost:13001 (admin / admin) |
| Jaeger | http://localhost:16686 |
| Loki | http://localhost:13100 |
| VictoriaLogs | http://localhost:9428 |
| OpenSearch API | http://localhost:9200 |
| OpenSearch Dashboards | http://localhost:5601 |
| PostgreSQL | localhost:15432 |
| Kafka / Redpanda | localhost:19092 |
| OTLP gRPC | localhost:4317 |
| OTLP HTTP | localhost:4318 |
curl -X POST http://localhost:5086/api/orders \
-H "Content-Type: application/json" \
-d '{"orderNumber":"ORD-001","description":"Test order"}'В ответе будет идентификатор заказа. Он понадобится для смены статуса и проверки карточки.
curl -X PATCH http://localhost:5086/api/orders/{ORDER_ID}/status \
-H "Content-Type: application/json" \
-d '{"status":"InProgress"}'Допустимая цепочка статусов:
New -> InProgress -> Delivered
\ /
------> Cancelled
Финальные статусы: Delivered и Cancelled.
docker compose exec postgres psql -U postgres -d order_tracking \
-c "SELECT id, type, status, occurred_at FROM outbox_messages ORDER BY occurred_at DESC LIMIT 5;"docker compose logs worker | grep "Kafka published"docker compose logs api | grep "Broadcasted status update"cd tools
npm install
node signalr-client.jsПосле изменения статуса клиент получает realtime-событие orderStatusChanged.
| Метод | Эндпоинт | Назначение |
|---|---|---|
POST |
/api/orders |
создать заказ |
GET |
/api/orders |
получить список заказов |
GET |
/api/orders/{id} |
получить заказ по идентификатору |
PATCH |
/api/orders/{id}/status |
изменить статус заказа |
OpenAPI-контракт хранится в src/OrderTracking.Presentation.Api/openapi.yaml. На его основе NSwag генерирует базовый контроллер OrdersControllerBase.g.cs, а ручная реализация находится в OrdersController.cs.
| Элемент | Значение |
|---|---|
| Hub | /hubs/orders |
| Методы подписки | JoinOrdersList(), JoinOrder(orderId) |
| Событие клиента | orderStatusChanged |
SignalR используется не вместо Kafka, а поверх событийной цепочки: Kafka доставляет событие между backend-компонентами, SignalR доставляет уже готовое обновление браузеру или другому realtime-клиенту.
Проект собирает три вида сигналов:
| Сигнал | Где смотреть | Что показывает |
|---|---|---|
| Метрики | Prometheus, Grafana | health, scrape, HTTP, Kestrel, runtime .NET, GC, thread pool, бизнес-счетчики |
| Логи | Loki, VictoriaLogs, OpenSearch, Grafana Explore | outbox, EF Core, Kafka, broadcast, trace/span context |
| Трейсы | Jaeger | HTTP-запросы, фоновые операции worker, Kafka/SignalR-цепочки |
OpenTelemetry Collector принимает данные от API и worker, после чего разводит их по специализированным хранилищам: Prometheus для метрик, Loki/VictoriaLogs/OpenSearch для логов и Jaeger для трассировок.
Prometheus успешно видит оба приложения: order-tracking-api и order-tracking-worker. На скриншоте оба target находятся в состоянии UP, значит endpoints /metrics доступны и scrape проходит без ошибок.
Grafana Explore в режиме Metrics показывает Prometheus-метрики без ручного написания PromQL. На общем экране видны маршрутизация ASP.NET Core, DNS, исключения, сборки .NET, GC и аллокации. Это быстрый способ понять, что приложение живое и генерирует телеметрию.
Дополнительный кадр того же набора метрик показывает соседние runtime-показатели и подтверждает стабильное поступление данных после старта приложения.
Глубокий экран .NET runtime полезен для анализа памяти, JIT, GC pause, CPU, working set и thread pool. По этим графикам видно старт приложения, прогрев JIT и дальнейшее стабильное состояние под demo-нагрузкой.
Метрики исходящего HTTP показывают активные запросы, соединения, длительность, очереди и bucket-распределения. В проекте они появляются из demo traffic и внутренних HTTP-вызовов.
Метрики входящего HTTP и Kestrel показывают активные запросы, длительность обработки, соединения и доменные счетчики каталога заказов.
Отдельно вынесены продуктовые счетчики: созданные и завершенные заказы, обновления статусов, риск SLA, открытая рабочая очередь, публикации outbox в Kafka и ошибки публикации.
Отдельный экран scrape-метрик подтверждает состояние наблюдаемости: scrape_duration_seconds, количество samples, target_info, otel_scope_info и up = 1.
В Loki удобно искать прикладные события. Запрос по Broadcasted показывает, что API получил событие и отправил realtime-обновление клиентам. В логах также видны traceid и spanid, поэтому запись можно связать с трассировкой.
VictoriaLogs в Grafana показывает те же события в другом backend-хранилище. На примере worker видны SQL-запросы к outbox_messages, включая выборку с FOR UPDATE SKIP LOCKED.
Встроенный интерфейс VictoriaLogs VMUI позволяет отдельно проверить поток логов worker и убедиться, что сервис получает записи напрямую.
Сводный пример LogQL/LogsQL показывает, что Loki и VictoriaLogs могут использоваться параллельно: один и тот же поток событий доступен в разных интерфейсах.
Jaeger показывает распределение запросов по времени и длительности. Для order-tracking-api видны HTTP-запросы, health-check и операции order_tracking.
Список последних трасс API позволяет открыть конкретный запрос и посмотреть связанные spans.
Для order-tracking-worker видны фоновые операции обработки outbox и публикации событий. Это помогает отследить путь от записи в БД до Kafka.
В репозитории заведена папка docs/screenshots, на которую ссылается README и дополнительные документы. Ожидаемые файлы:
| Файл | Содержание |
|---|---|
metrics-prometheus-targets.png |
состояние Prometheus targets |
metrics-grafana-explore-runtime-overview.png |
обзор runtime-метрик |
metrics-grafana-explore-runtime-overview-alt.png |
дополнительный кадр runtime-метрик |
metrics-grafana-explore-dotnet-deep.png |
GC, JIT, CPU, память, thread pool |
metrics-grafana-explore-http-outbound.png |
исходящий HTTP |
metrics-grafana-explore-http-server-business.png |
входящий HTTP, Kestrel, бизнес-счетчики |
metrics-grafana-explore-business-counters.png |
продуктовые счетчики заказов, Kafka и outbox |
metrics-grafana-explore-scrape-target-health.png |
scrape и health target |
logs-grafana-loki-broadcasted.png |
Loki-запрос по broadcast-событиям |
logs-grafana-victorialogs-outbox.png |
VictoriaLogs в Grafana, outbox и EF Core |
logs-victorialogs-vmui-worker.png |
VMUI VictoriaLogs |
traces-jaeger-search-api-scatter.png |
Jaeger scatter для API |
traces-jaeger-search-api-list.png |
список трасс API |
traces-jaeger-search-worker.png |
трассы worker |
Если скриншоты нужно переснять из работающего стека, используйте Playwright-утилиту:
cd tools/doc-screenshots
npm run setup
npm run captureНа Windows можно запустить готовый wrapper:
tools\doc-screenshots\capture.cmdПосле пересъемки PNG должны остаться с теми же именами, чтобы ссылки в markdown не ломались.
Локальная проверка backend:
dotnet restore OrderTracking.sln
dotnet format OrderTracking.sln --verify-no-changes
dotnet build OrderTracking.sln -c Release
dotnet test OrderTracking.sln -c Release --no-buildЛокальная проверка frontend:
cd frontend
npm ci
npm run ciПолезные скрипты:
bash scripts/ci-local.shpwsh -File scripts/ci-local.ps1GitHub Actions выполняет несколько групп проверок: lint для workflow и Dockerfile, .NET restore/build/test/format, frontend audit/lint/build, проверку markdown-asset'ов, Docker smoke-тесты, Trivy и CodeQL.
| Документ | Назначение |
|---|---|
docs/ci-cd.md |
CI/CD, GitHub Actions и проверки |
docs/testing.md |
тестовая стратегия |
docs/logs-query-languages.md |
LogQL, LogsQL, Lucene и DQL |
docs/traces-jaeger.md |
работа с трассировками в Jaeger |
docs/grafana-dashboard.md |
Grafana dashboard и панели наблюдаемости |
docs/screenshots/README.md |
генерация и обновление PNG-скриншотов |
CONTRIBUTING.md |
правила разработки и локальные проверки |
SECURITY.md |
безопасность, зависимости и секреты |
.
├── src/
│ ├── OrderTracking.Domain
│ ├── OrderTracking.Contracts
│ ├── OrderTracking.Application
│ ├── OrderTracking.Infrastructure
│ ├── OrderTracking.Presentation.Api
│ └── OrderTracking.Presentation.Worker
├── frontend/
├── tests/
├── deploy/
├── docs/
├── scripts/
├── tools/
├── docker-compose.yml
└── OrderTracking.sln
Лабораторная работа · Инструментальные средства разработки ПО · ИТМО














