ADR-0008: OpenAPI tags scope the generated API interfaces
Every ODD Platform OpenAPI operation carries exactly one tag, and the generator emits one Java interface per tag — so a tag is the unit that shapes the generated *Api interfaces.
Status
Accepted. Reconstructed from the codebase on 2026-05-30; the decision is live in the source today.
Context
ADR-0001 establishes that controllers implement OpenAPI-generated *Api interfaces rather than carrying their own HTTP mappings. That raises a follow-on question the spec must answer: what determines how those generated interfaces are carved up? With the spring generator, the answer is the OpenAPI tag — but only if the spec uses tags consistently. If operations carried several tags, or none, the generated interface boundaries would be ambiguous or arbitrary.
ODD's openapi.yaml adopts a deliberate tagging convention so the generated interfaces are predictable.
Decision
Every operation carries exactly one tag, and the generator is configured to emit one interface per tag (useTags: true). A tag is therefore the unit that shapes the generated API surface: tag alert produces AlertApi, tag search produces SearchApi, tag dataEntity produces DataEntityApi, and so on. Across all 194 operations in the spec there are no multi-tagged and no untagged operations, so every operation lands in exactly one generated interface — there is no ambiguity about which interface an endpoint belongs to.
Tags are resource-oriented, and for most resources the tag also lines up with a single URL prefix. Of the 33 tags, 30 group operations that all sit under one /api/<resource> path prefix (for example every alert operation is under /api/alerts, every search operation under /api/search). For those resources the tag, the URL prefix, and the generated interface coincide cleanly.
Three tags deliberately group a resource whose operations span more than one path root, so the tag is broader than a single URL prefix:
dataEntity(the largest, ~37 operations) spans/api/dataentities/**and/api/dataentitygroups/**— the entity and its group sub-resource.ownerAssociationRequestspans/api/owner_association_request/**and the owner-mapping operations under/api/owners/**.dataCollaborationspans/api/datacollaboration/**and the message-resolution operation under/api/messages/**.
For these, the tag is the resource boundary and the generated interface aggregates operations across the related paths. The invariant the spec actually holds is one tag per operation → one interface per tag; the "one tag = one URL prefix" alignment is the common case (30 of 33), not a universal rule.
Consequences
A contributor editing
openapi.yamlcontrols which generated interface an endpoint belongs to by setting its tag; the controller then implements that interface (per ADR-0001). Tag choice is an interface-design decision, not cosmetic.The generated
*Apiinterfaces are resource-shaped and stable, which keeps controllers small and their grouping predictable.For the three multi-root tags, an endpoint's URL prefix does not by itself tell you its interface — the tag does. A reader mapping a path to its generated interface must read the operation's tag, not infer it from the URL.
Because grouping is driven entirely by the tag, a missing or wrong tag on a new operation would misplace it in the generated surface; the single-tag-per-operation discipline is what keeps the mapping unambiguous.
Evidence
odd-platform-api-contract/build.gradle:10,24—generatorName = "spring"withconfigOptionsincludinguseTags: "true", which makes the generator emit one interface per tag;:16setsapiPackage = "org.opendatadiscovery.oddplatform.api.contract.api"and:44wirescompileJava.dependsOn tasks.openApiGenerate.odd-platform-specification/openapi.yaml— across all 194 operations, each declares exactly onetags:entry (no operation is multi-tagged or untagged); 33 distinct tags are used.Clean single-prefix examples: every
alertoperation is under/api/alerts(e.g.getAllAlertsonGET /api/alerts); everysearchoperation under/api/search(e.g.searchonPOST /api/search).Multi-root tags:
dataEntityoperations appear under both/api/dataentities/**and/api/dataentitygroups/**;ownerAssociationRequestunder/api/owner_association_request/**and/api/owners/**;dataCollaborationunder/api/datacollaboration/**and/api/messages/**.
See also
ADR-0001 — Contract-first HTTP layer — controllers implement these generated
*Apiinterfaces; this record explains how the spec decides what those interfaces are.
Last updated