4.3 KiB
Frontend & Backend Output Shapes – Clean Architecture (Strict, Final)
This document defines the exact responsibilities, naming, and placement of all data shapes involved in delivering data from Core → API → Frontend UI.
It resolves all ambiguity around Presenters, View Models, DTOs, and Output Ports. There is no overlap of terminology across layers.
⸻
- Core Layer (Application / Use Cases)
Core Output Ports (formerly “Presenters”)
In the Core, a Presenter is not a UI concept.
It is an Output Port that defines how a Use Case emits its result.
Rules • Core Output Ports: • define what data is emitted • do not store state • do not expose getters • do not reference DTOs or View Models • Core never pulls data back from an output port • Core calls present() and stops
Naming • *OutputPort • *Result (pure application result)
Example
export interface CompleteDriverOnboardingResult { readonly success: boolean; readonly driverId?: string; readonly error?: string; }
export interface CompleteDriverOnboardingOutputPort { present(result: CompleteDriverOnboardingResult): void; }
The Core does not know or care what happens after present() is called.
⸻
- API Layer (Delivery / Adapter)
API Presenters (Response Mappers)
API Presenters are Adapters.
They: • implement Core Output Ports • translate Core Results into API Response DTOs • store response state temporarily for the controller
They are not View Models.
Rules • API Presenters: • implement a Core Output Port • map Core Results → API Responses • may store state internally • API Presenters must not: • contain business logic • reference frontend View Models
Naming • *Presenter or *ResponseMapper • Output types end with Response or ApiResponse
⸻
- Frontend Layer (apps/website)
View Models (UI-Owned, Final Form)
A View Model represents fully prepared UI state.
Only the frontend has Views — therefore only the frontend has View Models.
Rules • View Models: • live only in apps/website • accept API Response DTOs as input • expose UI-ready data and helpers • View Models must not: • contain domain logic • validate business rules • perform side effects • be sent back to the server
Naming • *ViewModel
⸻
- Website Presenters (DTO → ViewModel)
Website Presenters are pure mappers.
They: • convert API Response DTOs into View Models • perform formatting and reshaping • are deterministic and side-effect free
They are not Core Presenters.
Rules • Input: API DTOs • Output: View Models • Must not: • call APIs • read storage • perform decisions
⸻
- API Client (Frontend)
The API Client is a thin HTTP layer.
Rules • Sends HTTP requests • Returns API DTOs only • Must not: • return View Models • contain business logic • format data for UI
⸻
- Website Services (Orchestration)
Website Services orchestrate: • API Client calls • Website Presenter mappings
They are the only layer allowed to touch both.
Rules • Services: • call API Client • call Website Presenters • return View Models only • Components never touch API Client or DTOs
⸻
- Final Data Flow (Unambiguous)
Core Use Case → OutputPort.present(Result)
API Presenter (Adapter) → maps Result → ApiResponse
API Controller → returns ApiResponse (JSON)
Frontend API Client → returns ApiResponse DTO
Website Presenter → maps DTO → ViewModel
UI Component → consumes ViewModel
⸻
- Terminology Rules (Strict)
Term Layer Meaning OutputPort Core Use case output contract Result Core Pure application result Presenter (API) apps/api Maps Result → API Response Response / ApiResponse apps/api HTTP transport shape Presenter (Website) apps/website Maps DTO → ViewModel ViewModel apps/website UI-ready state
No term is reused with a different meaning.
⸻
- Non-Negotiable Rules • Core has no DTOs • Core has no View Models • API has no View Models • Frontend has no Core Results • View Models exist only in the frontend • Presenters mean different things per layer, but: • Core = Output Port • API = Adapter • Website = Mapper
⸻
- Final Merksatz
The Core emits results. The API transports them. The Frontend interprets them.
If a type tries to do more than one of these — it is incorrectly placed.