HTTP Endpoint Refactor Audit¶
Goal¶
Bring the project’s HTTP surface to one hard standard, in the same way the repository already standardized the persistence layer.
This is not a cosmetic pass over views.py. It is a direct cutover that must:
- remove transport logic that expanded inside endpoint modules;
- split reads, writes, jobs, webhooks, and streaming into separate HTTP adapters;
- unify route prefixes, error mapping, response contracts, and operation endpoints;
- introduce one versioned API root:
/api/v1; - adopt an
async-func-firstendpoint style; - avoid preserving old endpoint-helper patterns when they block the target structure;
- eliminate giant
views.pyfiles acting as a second application layer.
Cutover Progress¶
The repository has already moved major parts of the surface to the new HTTP standard.
Delivered:
- root router tree moved to
backend/src/api/router.py->backend/src/api/v1/router.py; - domain-local router assembly via
build_router(mode, profile); - one versioned API root under
/api/v1; - root bootstrap now mounts only the API root router;
- schema generation and bootstrap tests now enforce
operationIduniqueness, tag discipline, and mode-specific exposure; - active runtime HTTP surface no longer depends on legacy
views.pymodules as the main integration point.
Removed:
- scattered top-level route assembly in bootstrap;
- multiple competing endpoint-entry styles;
- implicit versionless public router exposure as the default shape.
Current HTTP Surface¶
Current state:
backend/src/core/bootstrap/app.pymounts only the root API router;backend/src/api/v1/router.pycentralizes the versioned/api/v1surface;- active domains are mounted through domain-local router builders;
- the surface is mode-aware and OpenAPI-governed.
The first structural gap is already closed:
- there is an explicit
/apiroot router; /v1lives below it;- domains are mounted only below
/api/v1.
Main Problems¶
1. No Unified Router-Structure Standard¶
The endpoint layer was historically assembled inconsistently:
- sometimes
APIRouter(prefix=...)was used cleanly, sometimes prefix ownership was smeared across decorators; - some modules contained only read endpoints, while others mixed reads, writes, jobs, onboarding, webhooks, and auth behavior;
- naming and route ownership were inconsistent.
Consequence:
- mass refactor of URL surface becomes harder;
- shared transport rules are harder to enforce;
- modules are harder to review as bounded HTTP adapters.
2. views.py Turned Into Transport God Modules¶
In several domains, views.py accumulated:
- many endpoint categories in one file;
- manual serialization and mapping;
- manual exception translation;
- orchestration that no longer belonged in the transport layer.
3. Transport Mapping Leaked Into the View Layer¶
Representative cases included:
- response DTO shaping inline in endpoint modules;
- operational payload assembly directly in HTTP files;
- transport composition mixed with cross-domain read orchestration.
That means:
- the adapter knows too much about internal data shape;
- serialization policy is not centralized;
- endpoint files become a mixture of transport, presentation, and orchestration.
4. Repeated Command-Endpoint Boilerplate¶
Many endpoints repeated the same sequence:
- verify existence through a query service;
- call an application service;
- catch a domain exception;
- translate it into
HTTPException; - call
await uow.commit(); - return a response DTO.
This should be standardized rather than duplicated in dozens of handlers.
5. Job / Admin / Onboarding / Webhook Endpoints Mixed With Public Read Surface¶
The same files often mixed:
- public read endpoints;
- operator or admin commands;
- background trigger endpoints;
- onboarding flows;
- webhook and ingest endpoints.
That destroys a clean boundary between public projections and operational control surfaces.
6. Inconsistent Response Contracts¶
Response shape used to vary between:
- typed read models;
- raw
dict[str, object]queue responses; - fallback serialization “return whatever validates” behavior;
- ad-hoc
202payloads; - bare
list[...]responses without pagination envelope.
7. Error-Handling Policy Was Not Centralized¶
Many modules translated domain exceptions into HTTPException inline.
The repository needed one stable model:
- shared base error framework;
- domain-local mapping extensions where necessary;
- no repeated
try/except -> HTTPExceptionboilerplate in handlers.
8. Some Endpoint Modules Held Runtime Mechanics Too¶
In a few places, endpoint files also performed:
- ingest-token extraction;
- native webhook orchestration;
- queue and runtime mechanics.
That logic belongs in dedicated transport helpers or service/adaptor modules, not in endpoint handlers.
9. URL Ownership and Resource Semantics Were Hard to Read¶
Many paths were hanging off /coins/{symbol}/... across multiple domains.
That is acceptable only if the system makes clear:
- what the canonical coin resource is;
- what are projections;
- what are analytical subresources;
- what are operational commands.
Without a standard, URL semantics become unclear.
Target HTTP Standard¶
0. Shared Routing Hierarchy¶
Target routing tree:
/api
/v1
/<domain>
Rules:
create_app()mounts only the root API router;src/api/router.pymounts only version routers;src/api/v1/router.pymounts only domain routers;- domain endpoint modules must not know about
/apior/v1; - the current standard introduces only
/api/v1, not speculative future versions.
1. Mandatory Shared HTTP Core¶
Per-domain api/ packages are not enough. The repository needs a shared transport foundation in core/http.
Shared core responsibilities:
contracts.py— common transport DTOs and base Pydantic schemas;errors.py— sharedApiErrorcontract and error-body factory;responses.py— typed helpers for201,202,204, list, page, and stream responses;presenters.py— presenter protocols and mapping helpers;command_executor.py— one command-execution flow with commit and error mapping;launch_modes.py— mode and profile-aware router rules;tracing.py— request, correlation, and causation propagation.
2. One api/ Package per Domain and One Public Router Entrypoint¶
Each domain should expose exactly one public HTTP entrypoint:
src/apps/<domain>/api/router.py
Inside the package, it may aggregate:
read_endpoints.pycommand_endpoints.pyjob_endpoints.pywebhook_endpoints.pystream_endpoints.py
But bootstrap/app.py must know only the domain root router.
3. Endpoint Files Must Be Async-Func-First and Contain Only Handlers¶
An endpoint module should contain real async handlers, not half-framework logic.
Allowed:
- route declaration
- typed request parsing
- dependency injection of already assembled application-facing interfaces
- typed response return
- domain/app error translation to HTTP
Forbidden:
- repository ownership
- business orchestration
- giant payload assembly
- raw queue dispatch
- hidden transport validation through unrelated helpers
4. Transport Contracts Only Through Pydantic Schemas¶
Rules:
- request bodies are defined through
BaseModel; - response contracts are defined through
BaseModel; response_modelis required for public endpoints except explicit204cases;- handlers do not return ORM models, arbitrary dicts, or mixed payloads as public contracts;
- presenters map read models or result objects into Pydantic schemas.
5. Dependency-Injection Policy for Endpoint Layer¶
Endpoints should receive only application-facing dependencies:
- read facade or query service for read endpoints;
- command service for command endpoints;
- job/operation service for job endpoints;
- ingest service for webhook endpoints;
- stream adapter/service for stream endpoints;
- access or auth dependencies when needed.
Wrong patterns:
- passing
AsyncSessioninto handlers; - passing raw repositories directly;
- assembling services manually inside handlers;
- injecting raw Redis or queue clients;
- using one giant service for unrelated scenarios.
6. Router-Assembly Contract and Mode-Aware Mounting¶
Router assembly must be deterministic.
Rules:
- bootstrap mounts only the root API router;
- version router mounts only domain routers;
- domains aggregate their own subrouters;
- mode gating does not spread into individual handlers;
- preferred contract:
build_router(mode: LaunchMode, profile: DeploymentProfile) -> APIRouter.
7. HTTP Adapter Responsibility Contract¶
HTTP adapters may only:
- parse request data;
- call query or application services;
- complete command transaction boundaries through shared helpers;
- map results through presenters;
- perform HTTP-level error mapping;
- return typed response contracts.
They must not:
- touch repositories directly;
- expose raw queue-dispatch mechanics;
- perform complex cross-domain composition without an explicit facade;
- assemble large operational payloads inline;
- parse transport headers ad hoc as business logic.
8. Separate Surfaces by Responsibility¶
At minimum:
- read surface
- command surface
- job or operation surface
- webhook or ingest surface
- stream surface
Additional separation is allowed when domain complexity requires it.
9. Command Endpoint Standard¶
Command endpoints should follow one shared flow:
- parse typed input;
- call application service;
- use a shared command executor for commit;
- map the result to a typed response;
- translate typed domain/app exceptions through the shared error framework.
Mixed styles are not acceptable.
10. Unified Response Contract Standard¶
201 Createdonly for creation of a canonical resource with a typed representation;202 Acceptedonly for queued/job/async trigger endpoints, through a typedAcceptedResponse;204 No Contentonly for delete, discard, or toggle-like operations with no response body;200 OKfor synchronous mutation results when a resource representation or explicit operation payload is required.
Collection responses must not default to bare list[...] when the result can grow materially.
11. Pagination / Filter / Sort Governance¶
Large collections need:
- an envelope with
items; - page or cursor metadata;
- applied filters;
- optional sort metadata.
Field naming must be consistent across domains:
symbol,timeframe,source_id,status,created_after,created_beforesort_by,sort_order
The same semantics must not be expressed as from_ts, start, and since in different places.
12. Resource / Projection / Operation / Job / Webhook / Stream Policy¶
Each endpoint must be classified as one of:
- resource
- projection
- operation
- job trigger
- webhook/ingest
- stream
Operation endpoints must not masquerade as CRUD.
13. Stream Endpoint Standard¶
Streaming is its own transport category.
A stream endpoint should:
- authenticate and gate access;
- call a stream adapter;
- return streaming output only.
It must not contain business orchestration or unrelated state shaping.
14. Error Mapping Standard¶
Use:
- shared base translator in
core/http/errors.py - domain-local extensions such as
src/apps/<domain>/api/errors.py
Policies for 400, 401, 403, 404, 409, 422, and 202/409 for already-running operations must be consistent.
15. OpenAPI Governance¶
OpenAPI is part of the definition of done.
The system must standardize:
- predictable response model names
- domain and surface-specific tags
- stable
operationIdnaming
16. Launch Modes as an Architectural Constraint on HTTP Surface¶
Supported modes:
fulllocalha_addon
Mode affects:
- available routers
- operational endpoints
- onboarding
- webhooks
- streams
- auth and access policy
Handlers must not decide mode availability ad hoc.
17. Mode-Aware Availability Matrix¶
Each domain needs an explicit matrix so the repository can:
- control exposure surface;
- test bootstrap deterministically;
- avoid hidden runtime branches;
- understand what is allowed in each mode.
18. Capability Model and Contract Audiences¶
Principal-grade APIs need capability-level artifacts, not only routes.
Each capability should define at least:
- audience
- exposure mode
- transport category
- idempotency model
- resource requirements
Typical audiences:
- public
- operator
- automation
- integration
19. Idempotency Policy¶
Every command and job trigger must have:
- a deduplication strategy;
- standardized already-running behavior;
- explicit
202,409, or idempotent no-op semantics.
20. Operation Resource Model¶
Significant async work must be a first-class resource.
Standard flow:
- client calls command or job endpoint;
- API returns
202 Accepted; - response includes
operation_id; - operation status is observable as a resource or stream update.
21. Concurrency and Mutation Semantics¶
Mutating endpoints must define:
- whether they are idempotent;
- whether they conflict with already-running operations;
- what happens under retry;
- whether they queue or reject when an active operation exists.
22. Error Taxonomy¶
HTTP surface must align to a stable platform error taxonomy.
The same underlying failure cause should not produce unrelated transport behavior in different domains.
23. Consistency / Freshness Semantics¶
Analytical surfaces must expose:
- generation time
- freshness semantics
- cache behavior
- any staleness policy
24. Tracing Contract¶
HTTP transport must carry:
- request ID
- correlation ID
- causation ID
These must be governed centrally rather than improvised per endpoint.
25. Deployment Profiles and HA-Specific Policy¶
HA-specific exposure must remain explicit and governed.
The HA embedded profile must not accidentally inherit the full operator/admin surface.
26. Lifecycle, SLO, Caching, and Review Governance¶
Endpoints should also be reviewed against:
- lifecycle expectations
- performance and caching behavior
- review and release governance
27. Governance Scope by Endpoint Class¶
Rules should be enforced differently where needed, but the classes themselves must be explicit and consistent.
28. No Fallback Serialization¶
No endpoint should rely on “serialize whatever this object looks like.”
If a public transport contract exists, it must be typed and explicit.
What To Do by Module¶
The refactor program already completed the main domain cutovers:
market_structure[done]control_plane[done]signals[done]news[done]market_data[done]hypothesis_engine[done]patterns[done]indicators[done]portfolio[done]predictions[done]system[done]
Recommended Work Order¶
- fix root routing and versioned API assembly
- standardize shared HTTP core
- split endpoint surfaces by responsibility
- standardize command execution and response contracts
- stabilize OpenAPI governance and mode-aware exposure
- finish domain-by-domain cutovers with no fallback compatibility layers
Migration Rules¶
- no giant compatibility shims that become the new norm;
- no preserving legacy
views.pyas the main domain entrypoint; - no raw transport leakage carried forward “until later”;
- each domain cutover should land in final form, not in half-clean intermediate state.
Definition of Done¶
The HTTP refactor is done only if:
- bootstrap mounts only the root API router;
- domain HTTP entrypoints are explicit and singular;
views.pyno longer acts as the public god-module pattern;- command execution is standardized;
- response contracts are typed and explicit;
- error mapping is centralized and domain-extendable;
- mode-aware availability is governed;
- OpenAPI naming and tags are stable;
- async work is exposed through first-class operation resources;
- CI and docs enforce the resulting standard.