Implements Evolith: Evolith ADR-0012 — Advanced Authorization, RBAC/ABAC and Evolith ADR-0008 — Progressive Multimodule Evolution & Gateway BFF. UMS retains this document as its GraphQL/REST hybrid API design record.
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-05-21 |
| Context | UMS Web App — API Communication Strategy |
| Deciders | Architecture Team |
The UMS system needs to support both flexible data querying (with nested relationships, filtering, and field selection) and clear transactional command semantics. Using a single API pattern for both leads to either over-fetching (REST) or unclear mutation semantics (GraphQL).
Adopt a GraphQL for Queries, REST for Commands hybrid pattern:
Frontend (React)
├── GraphQL Client (graphql-request v7)
│ ├── All queries use absolute URL: `${window.location.origin}/graphql`
│ ├── Typed queries generated from schema
│ └── Cached via TanStack Query
│
└── REST Client (Axios via httpClient.ts)
├── All mutations (POST, PUT, DELETE)
├── CSRF token injection for state-changing requests
├── Dev headers (X-User-Id, X-Language, X-Tenant-Id)
└── Error normalization via interceptors
Positive:
Negative:
/api and /graphqlsrc/infrastructure/http/httpClient.ts — Axios instance for REST commandssrc/infrastructure/http/graphqlClient.ts — GraphQL client for queriessrc/infrastructure/http/csrf.ts — CSRF token managementvite.config.ts — Proxy /api and /graphql to backendSplit queries and commands into two independently deployed API services — a dedicated GraphQL query service and a dedicated REST command service.
Rejected because:
/graphql vs /api/v1/...).When this decision should be revisited:
This alternative becomes valid when any of the following conditions are met:
| Trigger | Explanation |
|---|---|
| Read throughput consistently 10x write throughput | Independent horizontal scaling of the query tier becomes justified |
| Separate teams own query vs command surfaces | Conway’s Law makes the split natural, not forced |
| Migration toward microservices is initiated | Tier separation is a prerequisite step |
| Incompatible technology requirements emerge | e.g., query tier needs a different runtime or caching strategy |
SaaS-specific consideration — tenant load isolation:
In a multi-tenant SaaS context, heavy GraphQL queries from a large tenant could impact command latency (login, provisioning) if both share the same process. This risk is mitigated in the current architecture by:
Tier separation remains the correct escalation path if these controls prove insufficient at scale.
Use GraphQL exclusively — queries and mutations — eliminating the REST layer.
Rejected because: