Repository navigation
Doc gRPC Contracts
Сервисы вызывают друг друга напрямую по gRPC, минуя Gateway. Контракты лежат в src/Shared/TravelAgency.Contracts.Grpc/Protos/: identity.proto, catalog.proto, booking.proto, media.proto.
Клиент пишет в чат по заявке. Chat не знает, чья это заявка, поэтому спрашивает Booking:
Chat → booking-service:8081 BookingService/ValidateBookingAccess { booking_id: "…", user_id: "…" }
Booking → Chat { has_access: true }
Если has_access = false, Chat отвечает клиенту 403 (REST) или ошибкой хаба (SignalR).
- gRPC-сервер каждого сервиса слушает отдельный порт 8081 (HTTP/2 без TLS, внутри сети Docker). REST работает на 8080.
- Адреса задаются настройками
GrpcClients__<Сервис>ServiceUrl, напримерhttp://catalog-service:8081. - Каждый вызов несёт метаданные
x-internal-authс общим сервисным токеном (GrpcSettings__InternalServiceToken, на сервере переменнаяGRPC_INTERNAL_SERVICE_TOKEN). Проверяет егоGrpcAuthInterceptorизShared.Infrastructure; без токена ответUnauthenticated(код 16). - Через Gateway gRPC наружу не публикуется.
- После каждого деплоя смоук-проверка шлёт по одному вызову без токена на каждый сервер и ждёт ответ HTTP/2 с кодом 16 (см. Где и как работает).
| Вызывающий | Сервер | RPC | Когда |
|---|---|---|---|
| Booking | Catalog | GetTourSnapshot |
создание заявки, добавление в избранное |
| Booking | Catalog | GetTourSnapshotForExistingBooking |
создание предложения по заявке |
| Booking | Identity | GetUserSummary |
карточка заявки для менеджера и админа; проверка роли в ValidateBookingAccess
|
| Chat | Booking | ValidateBookingAccess |
чтение истории, вход в группу хаба, отправка сообщения |
| Catalog | Media | GetMediaFiles |
сохранение фото тура и публикация: владелец, размеры, ширина обложки |
| Catalog | Media | MarkMediaFilesPublic |
публикация тура и PUT images у уже опубликованного тура |
Сервис IdentityGrpc.
| RPC | Запрос | Ответ |
|---|---|---|
GetUserSummary |
user_id |
user_id, email, first_name, last_name, role
|
Сервис CatalogService.
| RPC | Запрос | Ответ |
|---|---|---|
GetTourSnapshot |
tour_id |
TourSnapshotResponse; found = true только для опубликованного тура |
GetTourSnapshotForExistingBooking |
tour_id |
тот же ответ; found = true для опубликованного и снятого тура, для черновика false
|
TourSnapshotResponse: tour_id, title, description, price, currency, duration_days, snapshot_taken_at, found.
Цена в снимке: самая низкая цена предложения, действующего сегодня; если такого нет, самая низкая цена среди всех предложений тура. Если предложений нет, price = 0, currency = "USD".
Сервис BookingService.
| RPC | Запрос | Ответ |
|---|---|---|
ValidateBookingAccess |
booking_id, user_id
|
has_access |
Логика: доступ есть у клиента заявки; иначе Booking спрашивает роль у Identity и даёт доступ Manager и Admin. Неизвестная заявка или пользователь: false.
Сервис MediaService.
| RPC | Запрос | Ответ |
|---|---|---|
GetMediaFiles |
ids[] |
files[]: id, owner_id, content_type, width, height, is_public, available_sizes (w200, w800, w1600), purpose (tour-image или general). Неизвестные и удалённые id пропускаются |
MarkMediaFilesPublic |
ids[] |
пусто. Ставит IsPublic = true только у активных файлов с purpose = tour-image. Другое назначение, неизвестные и удалённые id не меняются. Флаг никогда не снимается, повтор безопасен |
Поля в .proto не переименовываются и не переиспользуются по номерам: новые поля добавляются с новым номером, старые клиенты их игнорируют.
Система
API
Сервисы
Как реализовано
Справочное
- Видение продукта
- Дорожная карта
- Этап 1: что осталось
- Этап 2: расширение
- Этап 3: масштабирование
- Концепция сайта и интерфейса
- Шаблон ТЗ