> For the complete documentation index, see [llms.txt](https://docs.opendatadiscovery.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.opendatadiscovery.org/developer-guides/architecture-decision-log/adr-0008-openapi-tag-per-resource-scoping.md).

# ADR-0008: OpenAPI tags scope 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.
* `ownerAssociationRequest` spans `/api/owner_association_request/**` and the owner-mapping operations under `/api/owners/**`.
* `dataCollaboration` spans `/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.yaml` controls 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 `*Api` interfaces 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"` with `configOptions` including `useTags: "true"`, which makes the generator emit one interface per tag; `:16` sets `apiPackage = "org.opendatadiscovery.oddplatform.api.contract.api"` and `:44` wires `compileJava.dependsOn tasks.openApiGenerate`.
* `odd-platform-specification/openapi.yaml` — across all 194 operations, each declares exactly one `tags:` entry (no operation is multi-tagged or untagged); 33 distinct tags are used.
* Clean single-prefix examples: every `alert` operation is under `/api/alerts` (e.g. `getAllAlerts` on `GET /api/alerts`); every `search` operation under `/api/search` (e.g. `search` on `POST /api/search`).
* Multi-root tags: `dataEntity` operations appear under both `/api/dataentities/**` and `/api/dataentitygroups/**`; `ownerAssociationRequest` under `/api/owner_association_request/**` and `/api/owners/**`; `dataCollaboration` under `/api/datacollaboration/**` and `/api/messages/**`.

## See also

* [ADR-0001 — Contract-first HTTP layer](/developer-guides/architecture-decision-log/adr-0001-openapi-generated-controller-interfaces.md) — controllers implement these generated `*Api` interfaces; this record explains how the spec decides what those interfaces are.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.opendatadiscovery.org/developer-guides/architecture-decision-log/adr-0008-openapi-tag-per-resource-scoping.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
