Перейти к содержанию

Спецификация Gateway <-> gRPC сервисов

Внешний API: gateway (REST/GraphQL).
Внутренний transport: gateway -> доменные сервисы по gRPC.


1. Текущее состояние (as-is)

1.1 Gateway gRPC clients (реально подключены)

services/gateway/src/bff/grpc-bff.module.ts регистрирует клиентов:

  • AUTH_GRPC -> fairflow.auth.v1 (5001)
  • CONTROL_GRPC -> fairflow.control.v1 (5002)
  • CONTACT_GRPC -> fairflow.contact.v1 (5003)
  • COMPANY_GRPC -> fairflow.company.v1 (5004)
  • PIPE_GRPC -> fairflow.pipe.v1 (5005)
  • ORDERS_GRPC -> fairflow.orders.v1 (5006)
  • PRODUCT_GRPC -> fairflow.product.v1 (5007)
  • ACTIVITY_GRPC -> fairflow.activity.v1 (5008)
  • PLATFORM_GRPC -> fairflow.platform.v1 (5009)

1.2 Сервисы, подключённые в grpc-bff.module (новый пакет)

  • DOCUMENTS_GRPC -> fairflow.documents.v1 (5010)
  • REPORTS_GRPC -> fairflow.reports.v1 (5011)
  • AUTOMATION_GRPC -> fairflow.automation.v1 (5012)
  • SEARCH_GRPC -> fairflow.search.v1 (5013)
  • AUDIT_GRPC -> fairflow.audit.v1 (5014)
  • NOTIFICATION_GRPC -> fairflow.notification.v1 (5015)
  • BILLING_GRPC -> fairflow.billing.v1 (5016)

1.3 Политика ingress и trust

  • Frontend проксирует только /api на gateway.
  • JWT валидируется только в gateway.
  • Доменные сервисы доверяют только metadata от gateway + валидный service API key.
  • Прямые frontend-вызовы в доменные сервисы запрещены.

2. Project context policy (D2)

2.1 Где хранится project context

  • Канонический источник контекста проекта: store (host state), а не URL-prefix.
  • Для CRM-портфеля используются пути без /p/:pid (например /contacts, /deals, /orders).
  • /p/:pid/* используется только для project-settings маршрутов.

2.2 Что обязан делать gateway

Для project-scoped RPC gateway обязан прокидывать:

  • x-project-id
  • x-user-id
  • x-request-id
  • traceparent/x-trace-id
  • x-service-api-key + x-gateway-api-key-id

Если x-project-id отсутствует для project-scoped метода -> INVALID_ARGUMENT/PERMISSION_DENIED по контракту домена.


3. Внешние API-пути (as-is vs target)

3.1 As-is (фактическая совместимость с текущим frontend)

  • auth/control/CRM: /api/v1/*
  • GraphQL: /api/graphql

3.2 Target

  • Единый REST-префикс: /api/v1/{entity}
  • Gateway-only граница
  • gRPC как единственный внутренний синхронный transport

4. Матрица Gateway -> gRPC (канон)

Gateway surface gRPC сервис
/api/v1/auth/* fairflow.auth.v1.AuthService
/api/v1/workspaces, /api/v1/projects* fairflow.control.v1.*
/api/v1/contacts* fairflow.contact.v1.ContactService
/api/v1/companies* fairflow.company.v1.CompanyService
/api/v1/deals*, /api/v1/pipelines* fairflow.pipe.v1.*
/api/v1/orders*, /api/v1/order-types* fairflow.orders.v1.*
/api/v1/products* fairflow.product.v1.ProductService
/api/v1/activities* fairflow.activity.v1.ActivityService
platform endpoints fairflow.platform.v1.PlatformService
documents endpoints fairflow.documents.v1.DocumentsService
reports endpoints fairflow.reports.v1.ReportsService
automation endpoints fairflow.automation.v1.AutomationService
search endpoints fairflow.search.v1.SearchService
audit endpoints fairflow.audit.v1.AuditService
notification endpoints fairflow.notification.v1.NotificationService
billing endpoints fairflow.billing.v1.BillingService

5. Error/timeout/idempotency conventions

  • gRPC/HTTP mapping:
  • INVALID_ARGUMENT -> 400
  • UNAUTHENTICATED -> 401
  • PERMISSION_DENIED -> 403
  • NOT_FOUND -> 404
  • ALREADY_EXISTS -> 409
  • RESOURCE_EXHAUSTED -> 429
  • UNAVAILABLE -> 503
  • Каждый outbound RPC из gateway должен иметь deadline.
  • Для create/update с риском дубликатов использовать idempotency-key.

6. Связанные документы


7. Event contracts (RabbitMQ backbone)

Exchange: fairflow.events (topic)

  • Producers:
  • automation.rule.created|updated|deleted|executed
  • automation.event.hooked
  • Consumers:
  • automation.triggers <- automation.trigger, automation.rule.trigger
  • search.indexer <- automation.rule.*, automation.event.*, audit.event.*, crm.*
  • audit.events <- automation.rule.*, automation.event.*, crm.*, gateway.*

Recommended payload envelope:

{
  "project_id": "string",
  "event_name": "string",
  "entity_type": "string",
  "entity_id": "string",
  "actor_id": "string",
  "actor_type": "string",
  "request_id": "string",
  "trace_id": "string",
  "payload": {}
}