Перейти к основному содержимому

Практикум — сценарий и архитектура OrderDesk

Разработчику Архитектору

Практикум, шаг 1 из 8. Перед кодом фиксируем зачем нужны два сервиса и как они общаются. Теория — REST, проектирование API, реактивные транспорты.


Бизнес-задача

OrderDesk — учебный мини-склад интернет-магазина:

  1. Менеджер заводит товары и остатки (каталог).
  2. Оператор оформляет заказ на несколько позиций.
  3. При создании заказа система резервирует остаток в каталоге.
  4. Клиенты (веб, Postman) получают живые уведомления о смене статуса заказа.

Один монолит на двух языках здесь не нужен: в реальных системах каталог и заказы часто развиваются разными командами и релизными циклами. Практикум показывает интеграцию через контракт, а не через общую базу.


Два сервиса

СервисЯзыкОтветственность
catalog-apiPython (FastAPI)CRUD товаров, остаток, резервирование и отмена резерва
orders-apiC# (ASP.NET Core)CRUD заказов, оркестрация резерва в каталоге, WebSocket для подписчиков

Правило границ: только catalog-api меняет поле stockAvailable. orders-api хранит заказы и статусы (draftreservedconfirmed / failed), но остатки запрашивает по HTTP.


Диаграмма взаимодействия


Почему REST между сервисами, WebSocket к клиенту

  • REST между orders-api и catalog-api — запрос-ответ, понятные коды ошибок, кэширование GET, идемпотентный DELETE резерва. Подходит для синхронной оркестрации «создать заказ → зарезервировать».
  • WebSocket от orders-api к клиенту — push без опроса: статус заказа меняется редко, но UI должен узнать сразу. Подробнее о выборе транспорта — в 116.

В базовой версии практикума WebSocket поднимается на orders-api; каталог остаётся внутренним доменным сервисом по REST. При расширении можно добавить SSE или webhook из каталога в заказы (шаг 7).


Нефункциональные требования

ТребованиеРешение в практикуме
Версионирование APIПрефикс /api/v1
БезопасностьJWT для клиентов, заголовок X-Api-Key между сервисами
МасштабированиеStateless HTTP; сессии WebSocket привязаны к экземпляру (sticky sessions или Redis backplane — в проде)
НаблюдаемостьX-Request-Id сквозь оба сервиса, структурированные логи
ОтказоустойчивостьТаймаут HttpClient, повтор только для идемпотентных операций

Структура репозитория (локально)

Создайте папку OrderDesk рядом с учебными проектами:

OrderDesk/
├── catalog-api/ # Python
│ ├── app/
│ │ ├── main.py
│ │ ├── models.py
│ │ ├── schemas.py # DTO (Pydantic)
│ │ └── db.py
│ └── requirements.txt
└── orders-api/ # C#
├── Program.cs
├── Models/
├── Dtos/
└── appsettings.json

Код появится в шагах 4 и 5. Следующий шаг — контракт API без привязки к языку: Проектирование контракта.


Роли и доверие

┌─────────────────┐
Публичный JWT │ orders-api │ X-Api-Key (сервисный)
Postman / SPA ───►│ заказы, WS │──────────────────────────► catalog-api
└─────────────────┘

SQLite orders
ЗонаКто вызываетАутентификация
Каталог, CRUD товаровМенеджер, PostmanJWT (опционально в учебной версии) или внутренняя сеть
РезервыТолько orders-apiX-Api-Key
Заказы, WebSocketОператор, UIJWT Bearer / access_token в query для WS

Сверьте потоки в интерактивной песочнице — кнопки «Сквозной сценарий» и «Catalog down» показывают те же коды, что ожидаются в Postman.


Типовые сбои и ответ системы

СитуацияСимптом для клиентаГде чинить
Каталог недоступен502 Bad Gateway на POST /ordersТаймаут HttpClient, health-check catalog
Двойной клик «Оформить»Два заказа или один (с Idempotency-Key)шаг 6
Резерв есть, оплата отмененаОстаток «завис»POST …/cancel + DELETE резерва в каталоге
WebSocket оборвалсяUI отстаёт от RESTReconnect + GET /orders/{id} для синхронизации (шаг 7)

Критерии готовности шага 1

  • Нарисована диаграмма из трёх участников (клиент, orders, catalog).
  • Понятно, какое поле (stockAvailable) владеет только каталогом.
  • Выбраны порты 8100 / 5200 и префикс /api/v1.

См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").