Decide OpenAPI schema strategy for domain newtypes (keep domain I/O-free) #3

Closed
opened 2026-06-02 04:26:47 +00:00 by logaritmisk · 1 comment
Owner

The domain crate is intentionally I/O-free (only uuid + serde). When real entities start surfacing through the API, newtypes like OrgId will need utoipa::ToSchema to appear in the OpenAPI document — but we don't want to compromise domain's purity.

Decide the approach before the first entity lands:

  • Option A: Allow the utoipa derive in domain (it's a non-I/O, schema-only dependency).
  • Option B: Keep domain free of utoipa and define ToSchema wrappers/DTOs in the api crate.

Trade-off: A is less boilerplate but widens domain's dependency surface; B keeps domain minimal but adds a mapping layer. Pick one and document it in the architecture spec.

Source: Foundation (Plan 0) final review — forward-looking.

The `domain` crate is intentionally I/O-free (only `uuid` + `serde`). When real entities start surfacing through the API, newtypes like `OrgId` will need `utoipa::ToSchema` to appear in the OpenAPI document — but we don't want to compromise `domain`'s purity. Decide the approach before the first entity lands: - **Option A:** Allow the `utoipa` derive in `domain` (it's a non-I/O, schema-only dependency). - **Option B:** Keep `domain` free of `utoipa` and define `ToSchema` wrappers/DTOs in the `api` crate. Trade-off: A is less boilerplate but widens `domain`'s dependency surface; B keeps `domain` minimal but adds a mapping layer. Pick one and document it in the architecture spec. _Source: Foundation (Plan 0) final review — forward-looking._
Author
Owner

Decision: Option A — allow the utoipa::ToSchema derive in domain.

Rationale: domain already carries serde/serde_json/time (data-shape deps, no real I/O), and utoipa is a schema-description macro in that same family — not runtime I/O (no axum/sqlx). Visibility and AuthorityKind already derive Serialize/Deserialize with #[serde(rename_all = "lowercase")], so adding ToSchema is a one-word change that yields proper string enums with no duplicate enum set to maintain in api. The mapping layer that Option B would add (parallel enums + domain→api conversion) isn't worth it for fixed ≤7-variant sets.

Scope of the rule: domain may derive ToSchema (and use #[schema(...)] attrs) on pure data types. It must NOT gain runtime-I/O deps (axum, sqlx, etc.) — the api crate still owns HTTP/DB concerns and its own request/response View DTOs.

Implemented alongside #29 (enum-typing) and #24 (open-map fields). Closing.

**Decision: Option A** — allow the `utoipa::ToSchema` derive in `domain`. Rationale: `domain` already carries `serde`/`serde_json`/`time` (data-shape deps, no real I/O), and `utoipa` is a schema-description macro in that same family — not runtime I/O (no axum/sqlx). `Visibility` and `AuthorityKind` already derive `Serialize`/`Deserialize` with `#[serde(rename_all = "lowercase")]`, so adding `ToSchema` is a one-word change that yields proper string enums with **no duplicate enum set** to maintain in `api`. The mapping layer that Option B would add (parallel enums + domain→api conversion) isn't worth it for fixed ≤7-variant sets. Scope of the rule: `domain` may derive `ToSchema` (and use `#[schema(...)]` attrs) on pure data types. It must NOT gain runtime-I/O deps (axum, sqlx, etc.) — the `api` crate still owns HTTP/DB concerns and its own request/response View DTOs. Implemented alongside #29 (enum-typing) and #24 (open-map `fields`). Closing.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logaritmisk/biggus-dickus#3