API boundary layout
Directive¶
All inbound HTTP lives in one api package with a fixed, versioned shape. Request and response
schemas live under api/<version>/schemas/{requests,responses}/, and routers and endpoints under
api/<version>/routers/. A router module defines no schema. A request schema is named
<Thing>Request and a response schema <Thing>Response. The api package is the only inbound
boundary: core and database never import it.
Why¶
- A predictable layout lets tooling reason about code by placement instead of guesswork: CES-4
(
api-schemas-extra-forbid) fires only on models underapi/**/schemas/..., so it stays silent for internal models without inferring intent. - Versioned subpackages (
v1,v2) make breaking API changes additive: a new version is a new directory, not an edit that ripples through shared modules.
Layout¶
myapp/
api/
v1/
routers/
users.py
schemas/
requests/
create_user.py # BaseModel + ConfigDict(extra="forbid") (CES-4)
responses/
user.py
core/ # never imports api
database/ # never imports api
Enforcement¶
The rules of botica that depend on the layout read it from the path of each file. The direction,
that nothing below api imports api, is the rule import-layers (CES-5) with api in its
layers.
Related¶
- CES-4 (
api-schemas-extra-forbid): the schema rule scoped to this layout. - CES-5 (
import-layers): the layer direction that protects this boundary.
Rules¶
api-schema-in-route-module(BTFAST002)api-request-schema-name(BTFAST003)api-response-schema-name(BTFAST004)