Data Flow
How data moves in and out of the Feyenoord platform: synchronous request/response paths, the API gateway and GraphQL router, client SDKs, and asynchronous event flows.
For the full node map and external integrations, see Platform Overview.
Request paths
Client apps reach backend services through a single edge endpoint (Traefik). Which path a request takes depends on the API it targets.
Federated GraphQL (v2)
Used for new domain services composed into one graph.
Client → Traefik (/v2/graphql) → Apollo Router → subgraph(s) → database / external APIThe router receives a single GraphQL operation, builds a query plan, and dispatches field fetches to the relevant subgraphs: Identity, Notifications, and Loyalty today. It stitches the results into one response. Subgraphs own their domain's schema, resolvers, and data access.
See Federation Patterns for entity design and cross-subgraph extension rules.
Legacy monolith (v1)
The existing platform API.
Client → Traefik (/api/v1, /api/graphql) → Legacy Monolith API → PostgreSQL / Redis / external APIsThe monolith exposes both REST (/api/v1/...) and GraphQL (/api/graphql). It remains the primary backend for web and native until domains migrate to federated subgraphs.
Subgraph REST (OpenAPI)
Identity and Notifications also expose typed REST endpoints for direct service-to-service or tooling access.
Client / tool → Traefik (/identity, /notifications) → Dapr sidecar → service → databaseTraefik rewrites these paths to Dapr service-invocation calls on each app's sidecar.
API Gateway (Traefik)
Traefik is the only entry point clients need to know about. It handles:
- Path-based routing:
/api/v1and/api/graphqlto the legacy API;/v2to the Apollo Router;/identityand/notificationsto subgraph REST via Dapr - TLS termination in production
traceparentpropagation: W3C trace context forwarded to downstream services
Clients stay unaware of internal topology. See ADR-001.
GraphQL Router
The Apollo Router composes a supergraph from each subgraph's schema.graphql at startup (in CI via rover supergraph compose).
- Clients query one endpoint and one schema
- The router plans and parallelizes fetches across subgraphs
- No query namespacing: all fields live on the root
Querytype for single-pass planning
Client SDKs
Shared packages in packages/sdks/ give web and native typed access to backend APIs. Each SDK matches the transport of its backend.
| SDK | Transport | Generated from |
|---|---|---|
@repo/api-sdk | GraphQL (Apollo Client) | Legacy API schema (apps/api/generated/schema.graphql) |
@repo/identity-sdk | REST (openapi-fetch) | OpenAPI spec at /v2/identity/openapi.json via openapi-typescript |
@repo/notifications-sdk | REST (openapi-fetch) | OpenAPI spec at /v2/notifications/openapi.json via openapi-typescript |
@repo/sanity-sdk | Sanity client | Sanity schema |
The legacy GraphQL SDK uses a deliberately tuned Apollo InMemoryCache (possibleTypes + typePolicies) generated from the monolith schema. See API SDK (legacy).
OpenAPI SDKs regenerate types with:
# identity-sdk
pnpm dlx openapi-typescript http://localhost:8888/v2/identity/openapi.json -o ./src/schema.ts
# notifications-sdk
pnpm dlx openapi-typescript http://localhost:8888/v2/notifications/openapi.json -o ./src/schema.tsFederated GraphQL (v2) clients typically use Apollo Client pointed at /v2/graphql directly. Subgraph schemas are composed at the router, not consumed individually from clients.
Async event flow
Not all data flows are request/response. Live match events use an event-driven path:
StatsPerform SDDP → Match Gateway → Dapr pub/sub (Redis Streams) → Notifications → OneSignal → device- Match Gateway connects to the StatsPerform SDDP WebSocket feed during live matches
- It normalizes incoming events (goals, lineups, cards, etc.) and publishes them to Dapr pub/sub topics
- Notifications subscribes via its Dapr sidecar (
GET /dapr/subscribe) and handles incoming events - Push notifications are delivered through OneSignal; in-app notifications are stored in MongoDB
Dapr decouples publishers from subscribers. Swapping the message broker in production requires only a Dapr component change, not application code. See ADR-005: Message Queue and Docker Compose (Dapr Sidecars).
Sanity webhooks and the legacy API also trigger background work (Algolia indexing, content sync). Those paths are domain-specific and documented in their respective service guides.
Related
- Platform Overview: layered architecture and system diagram
- GraphQL Guide: schema conventions and federation patterns
- API SDK (legacy): Apollo cache configuration for the monolith
- Observability: trace propagation across these paths
- Docker Compose: local wiring and Dapr setup