> 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-0001-openapi-generated-controller-interfaces.md).

# ADR-0001: Contract-first HTTP layer

## Status

**Accepted.** Reconstructed from the codebase on 2026-05-30; the decision is live in the source today.

## Context

ODD Platform exposes a large REST surface (hundreds of endpoints across data entities, ingestion, search, lineage, RBAC, collaboration, and more). A platform that size needs one answer to a structural question: **where does the HTTP contract live, and how is it kept consistent** between the server that implements it and the clients (the web UI, collectors, push adapters) that call it?

There are two broad options: hand-write Spring MVC/WebFlux mappings on each controller, or define the contract once in a specification and generate the wiring from it. Hand-written mappings drift — the spec and the code diverge silently, and each client re-derives the contract by hand. ODD chose the contract-first path.

## Decision

**Every REST controller in `odd-platform-api` is a thin delegate that `implements` an OpenAPI-generated `*Api` interface.** The HTTP method, path, and `produces`/`consumes` annotations live on the generated interface — compiled from `openapi.yaml` into the `org.opendatadiscovery.oddplatform.api.contract.api.*` package by the `odd-platform-api-contract` module's `openApiGenerate` build step — **never on the controller class itself**.

A controller class carries only `@RestController` and `@RequiredArgsConstructor`; each method carries only `@Override` and delegates straight to a service. Consequently, **adding or changing an endpoint is a specification-edit-and-regenerate flow**, not a controller edit.

## Consequences

* `openapi.yaml` (in `odd-platform-specification`) is the **single source of truth** for the HTTP surface. The UI's generated TypeScript client and the server's generated interfaces stay in sync by construction.
* **To add an endpoint:** edit `openapi.yaml`, regenerate, then implement the `@Override`. Putting a `@PostMapping` / `@GetMapping` directly on a controller class does **not** follow the convention — it would create a route that lives outside the contract and silently drift the spec from the code.
* The generated `*Api` interfaces are **build artifacts** and are not committed to the source tree; to read the live contract, read `openapi.yaml`.
* **Two deliberate exceptions, both external-webhook receivers.** `AlertManagerController` is fully hand-coded — it bridges an external Alertmanager webhook payload that is not part of ODD's generated contract. `EventApiController` is also fully hand-coded — it receives the Slack events webhook via a direct `@PostMapping("/api/slack/events")`, again with no generated interface. Both are a single `@RestController` with a direct `@PostMapping` because each bridges an external system's webhook payload that is not part of ODD's contract — the deliberate exceptions that prove the rule.

## Evidence

* `odd-platform-api/.../controller/AlertController.java:11-17` — `@RestController @RequiredArgsConstructor public class AlertController implements AlertApi`; the class declares no `@RequestMapping`, and its methods are `@Override` with no mapping annotations.
* The interface is imported from `org.opendatadiscovery.oddplatform.api.contract.api.AlertApi` — generated by the `odd-platform-api-contract` module's `openApiGenerate` task at build time and absent from the source tree.
* `odd-platform-specification/openapi.yaml` — the contract the `*Api` interfaces are generated from.
* The same shape holds across the module's controllers (data-entity, ingestion, search, and RBAC controllers all `implements` their generated `*Api` with no controller-level mapping annotations). The two deliberate exceptions are the external-webhook receivers, both fully hand-coded with no generated `*Api`: `AlertManagerController.java:15-21` (`@RestController`, no `implements`, `@PostMapping("ingestion/alert/alertmanager")` with a `// TODO: define OpenAPI spec` note) and `EventApiController.java:14-22` (`@RestController`, no `implements`, `@PostMapping("/api/slack/events")` — the Slack events webhook).

## See also

* [How to contribute](/developer-guides/how-to-contribute.md) — the contribution workflow, including the spec-and-regenerate loop.
* [Build and run ODD Platform](/developer-guides/build-and-run/build-and-run-odd-platform.md) — building the module that runs the OpenAPI generation.


---

# 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-0001-openapi-generated-controller-interfaces.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.
