ADR-0016 — GraphQL is a protocols/ package, one-directional from frameworks/
Status: accepted
Context
ADR-0002 established two parent folders — orms/ (adapters) and frameworks/ (framework bindings) — and said no speculative packages get created beyond what a real feature needs. Adding GraphQL support raised a question ADR-0002 never had to answer: GraphQL is not an ORM adapter, and it is not quite a "framework binding" in the sense @kavo/nest is one. @kavo/nest bundles two things together — a host framework (Nest's DI, routing, decorators) and a wire protocol (REST, via decoration-time route generation). GraphQL is only the second thing: a wire protocol/API paradigm that could sit atop any host framework — Nest today, a future Express/Fastify/Next.js binding later — the same way REST does. Treating @kavo/graphql as a third frameworks/* sibling would conflate protocol with host framework and give it no natural way to reach @kavo/nest's discovery internals (getKavoEntities(), ModuleRef) without either package importing the other in both directions — exactly the frameworks/* <-> frameworks/* edge ADR-0002's boundary rules exist to prevent between adapter and framework.
Decision
A new parent folder, packages/protocols/, holds wire-protocol bindings that are host-framework-agnostic: @kavo/graphql is the first. These packages depend on @kavo/core only (dependency-cruiser: protocol-bindings-only-import-core) — exactly like an ORM adapter's constraint, never a host framework package.
Host framework packages (packages/frameworks/*) may depend on a protocol package to build framework-specific glue — @kavo/nest depends on @kavo/graphql to provide BaseKavoGraphQLController and createDefaultGraphQLController — but never the reverse. protocol-bindings-only-import-core enforces the reverse direction by blocking @kavo/graphql from importing packages/frameworks or @kavo/nest. The resulting dependency direction is frameworks/* → protocols/*, mirroring frameworks/* → core and orms/* → core: protocols and ORMs are both leaves adapting one concern; framework packages are the only ones allowed to depend sideways, and only toward protocols — adapters still meet frameworks only through Nest's DI container, unchanged from ADR-0002.
Cases:
@kavo/graphqlmay depend on@kavo/coreand thegraphqlpeer only.@kavo/nestmay depend on@kavo/core, its@nestjs/*peers, and@kavo/graphql— never an ORM adapter (framework-bindings-import-core-and-protocol-barrelsunchanged).- A future
@kavo/grpcfollows theprotocols/shape above. A future@kavo/express/@kavo/fastify/@kavo/nextjsfollows theframeworks/shape and may depend on whicheverprotocols/*package(s) it wants to offer glue for.
An entity opts into GraphQL exposure separately from its @Kavo config — registerKavoGraphQLTypes(Entity, {...}), not a @Kavo(Entity, { graphql: {...} }) option — because @kavo/graphql cannot know @Kavo exists (same reasoning ADR-0002 already gives for adapters never being reachable from framework config directly).
Consequences
@kavo/graphql's host-agnostic discovery helper (resolveKavoGraphQLSchema) is reusable verbatim by any future host framework binding — only "how to enumerate entities" and "how to resolve a bound service" differ per host, and both are supplied by the caller, never by@kavo/graphqlitself.- Two framework bindings for the same protocol (e.g. a future Nest and Express GraphQL glue) share code only through
@kavo/graphql, never with each other directly — theframeworks/* <-> frameworks/*edge stays forbidden. - ADR-0002's "no speculative packages" still holds:
protocols/graphqlis real, built, tested code, not a stub — a futureprotocols/grpcgets created when there is real work to put in it, not preemptively.