Server Controller Modules
server-controller modules are the thin adapters between server-interfaces and server-service. Their job is to translate a standardized internal request into validated DTOs, delegate the work to the service layer, and translate the service's structured result back into a standardized response. Controllers contain no business logic.
On This Page
- Purpose
- Design Principles
- Naming Convention
- Typical Files
- Per-Function Responsibilities
- Central Entry Rule
- Request Flow
- Boundary Rules
- Further Reading
Purpose
- Act as thin adapters between
server-interfacesandserver-service - Translate interface-specific input/output into service-friendly DTOs and back
- Validate input, build DTOs, delegate, map results - nothing more
- Convert the interface-specific request format into a standardized internal format and the service result back into a transport-neutral response
Design Principles
| Principle | Detail |
|---|---|
| Minimal and predictable | Controllers are 10-30 lines per action |
| No business or domain logic | Belongs in the service layer |
| No database access | Belongs in the helper modules used by services |
| No direct calls to external providers | Wrappers live in helper modules |
| Explicit inputs and outputs | Standard request format in, standard response out |
| Stateless and request-scoped | No long-lived state inside the controller |
| Consistent shape across controllers | Every controller receives (instance, request) and returns the same standardized response |
The interface layer converts the protocol-specific request (Express request, Lambda event, webhook payload) into the standard format. It passes execution context separately as the first argument. Controllers pass that same instance into services and helper modules.
Naming Convention
| Element | Convention |
|---|---|
| Module directory name | [entity-name] (singular) |
| Main file name | [entity-name].controller.js |
| Location | src/server/controller/ |
For example, the user controller lives at src/server/controller/user.controller.js.
Typical Files
| File | Purpose |
|---|---|
[entity].controller.js | Contains all controller functions for the entity. Each function represents a single action or use case |
index.js | (Optional) public export surface if the controller is split into multiple files |
Typical Function Names
getDataByID(instance, request)- fetch a single recordcreate(instance, request)- create a new recordupdate(instance, request)- partial updatesendOtp(instance, request)- trigger a one-time-pin flow
Per-Function Responsibilities
Each controller function does exactly six things, in this order:
- Required parameter checks - shape-level checks (is the request body present)
- Basic typecasting and trimming - normalize before validation
- Construct input DTOs for the service - using model-provided DTO builders
- Run domain validation - via
Lib.[Entity].validation.validate* - Invoke the corresponding service function - with
instancefirst and the validated DTO second - Map service results and errors to the standard response format - attach request metadata (request time, correlation ID)
If a function does anything beyond these six steps, that work belongs in the service layer.
Central Entry Rule
All server-side entry points (API, Hooks, Jobs, future Express routes) must pass through a controller. Controllers are the single, consistent entry layer into server logic.
The controller uses the base model and server model to:
- Perform domain sanitization (canonical formatting)
- Perform domain validation (rules, invariants)
It then creates input DTOs using model-provided DTO builders, invokes the corresponding service functions with the validated DTOs, and maps service outputs and errors to a transport-neutral response envelope.
Request Flow
Interfaces (API / Hook / Job)
-> Server Controller
-> Model / Model-Server (sanitize + validate)
-> Data creation (DTO builders)
-> Server Service (orchestration, business logic)
<- Service result envelope
<- Standard response envelopeBoth the request and response shapes are documented in server-interfaces.md.
Boundary Rules
Controllers may be used by
server-interfacesonly
Controllers must NOT
- Access database or repositories
- Contain orchestration or workflow logic (belongs to
server-service) - Call external providers directly
- Be reused by client applications
Controllers may
- Receive the request instance as the first argument and pass it unchanged to services
- Read input from the standardized request object
- Run validation via the model layer
- Build DTOs via the model layer
- Delegate to the service layer
- Format the service's structured result into the standard response
Further Reading
- Server Service Modules - where business logic lives
- Server Interfaces - the standardized request and response formats
- Model Modules - DTO builders and validation
- Validation Approach - how validation runs inside the controller
- Error Handling - how the controller forwards domain errors via
Lib.Functions.errorResponse