Files
clean-architecture-backend-…/docs/adr/ADR-GRPC-001-platform-family-and-registry-shape.md

3.0 KiB

ADR-GRPC-001: The gRPC platform ships as a registered family, not as one adapter leaf

  • Status: accepted
  • Date: 2026-08-30
  • Scope: :grpc:*, :grpc-advanced:*, src/config/architecture/modules.json

Context

The two source plans describe a type-safe gRPC execution platform with its own API, SPI, adapters and composition root: fifteen Stable modules under modules/grpc and sixteen Advanced ones under modules/grpc-advanced, on Gradle Kotlin DSL, in package io.backend.skeleton.grpc, against Spring Boot 4.1.

None of that layout exists here. This repository uses Groovy DSL, a fail-closed module registry that owns the leaf list, package root dev.caskeleton, and Spring Boot 4.0.8. The plans anticipate this: their last Global Constraint says that when the repository structure differs, file paths are remapped and the public contracts, invariants and test meanings are not changed.

Two shapes were available. Fold the platform into the existing :adapter:inbound:grpc leaf as packages — which is what the JPA, GraphQL, WebSocket and HTTP platforms did here — or register it as a family the way messaging:* is registered.

Decision

Register it as a family: twelve Stable leaves under src/grpc/ and six Advanced ones under src/grpc-advanced/.

The deciding property is that this is not a layer of this application. Root CLAUDE.md already describes messaging:* as "a vendored messaging platform: a product with its own API, SPI, adapters and composition boundary, not a layer of this application", and the gRPC platform is the same shape for the same reason — the application is meant to reach it the way it reaches a library, through an application-owned port. The four platforms that became packages are all layers of this application; this one is not.

The split between src/grpc/ and src/grpc-advanced/ is not organisational. The Stable plan requires that the Stable starter's build fail if it reaches an Advanced module, and separate Gradle path prefixes make that a verifyCleanArchitectureDependencies failure rather than a review note: grpc-spring-boot-starter's registry entry names no advanced id, and it cannot acquire one silently.

Consequences

The registry grew from 44 leaves to 62. That is a large registry change, made deliberately and in one place. Every new leaf is runtime_memberships: [], so nothing ships until a second, explicit decision moves it.

The advanced boundary is checked twice. Once by the registry at build time, and once by GrpcStableBuildInvariant at runtime, because a fat jar or a shaded artifact is assembled by something the registry never sees.

Four testkit modules became four test lanes. The plan's split exists so in-process results cannot be mistaken for network results; this repository expresses that with ca.strict-test-lane, whose lanes fail when they discover nothing and never serve an up-to-date result. GrpcEvidenceGrade keeps the same rule inside the code, so a report cannot cite a contract run as transport evidence.

Codegen is not wired. See ADR-GRPC-002.