DTO Guide
This guide explains how DTOs work in Vona within the Cabloy monorepo.
Why DTOs matter
DTOs are not only transport classes. In Vona, they are part of one contract system shared across:
- validation
- OpenAPI metadata
- serializer-facing response shape
- model-aware DTO inference
- frontend-facing generated contracts
That is why DTO design should be treated as a framework concern instead of only a controller-local convenience.
Create a DTO
Example: create a DTO named studentCreate in module demo-student.
npm run vona :create:bean dto studentCreate -- --module=demo-studentDTO definition
Representative pattern:
@Dto<IDtoOptionsStudentCreate>()
export class DtoStudentCreate {}DTOs in the backend contract loop
DTOs are the most explicit named contracts in the backend contract loop.
A useful split is:
- entities define reusable field/data structure close to persistence
- DTOs define explicit request/response contract artifacts
- inferred DTOs reduce duplication when the model/query shape is already strong enough
Read this guide together with:
@Api.field
DTO field definitions use the same @Api.field mental model as entities.
That means DTOs can express:
- validation rules
- field metadata
- OpenAPI-facing schema information
- serializer-oriented response metadata when needed
For response-shaping behavior built on the same field metadata surface, see Serialization Guide.
Representative pattern:
class DtoStudentCreate {
@Api.field(v.title($locale('Name')), v.min(3))
name: string;
@Api.field(v.title($locale('Description')), v.optional())
description?: string;
}DTO options
Three especially important DTO option areas are:
independentopenapifields
These options make DTOs configurable as reusable schema objects, not just local TypeScript classes.
A useful ownership rule is:
- DTO metadata defines the contract shape close to the class
- app config can still override broader DTO behavior
- inference tools can reduce how much hand-authored DTO code is needed
App-config override support
DTO options can also be configured through app config.
That matters because the DTO layer participates in the broader framework configuration system instead of being fully hardcoded in one file.
Mapped class tools
One of the most valuable DTO topics is reuse through mapped-class helpers.
Representative tools include:
$Class.pick$Class.partial$Class.omit$Class.mixin
These let you derive DTOs from existing entities or DTOs instead of re-declaring the same field sets repeatedly.
Operation-specific DTO thinking
A practical way to think about DTO families is by operation shape.
Common operation families include:
- create DTOs
- update DTOs
- get DTOs
- list-and-count DTOs
- query DTOs
- query-page DTOs
- aggregate DTOs
- group DTOs
Some of these are hand-authored DTO classes. Others are better expressed through Vona’s inference helpers.
The important point is not to force every operation shape into one generic DTO when the framework already distinguishes them more precisely.
Explicit DTOs vs inferred DTOs
A practical split is:
- use explicit DTO classes when the contract needs a stable named artifact
- use inferred DTOs when the contract closely follows model structure or query shape
- wrap inferred DTOs in a named DTO class when reuse or discoverability becomes more important
For the inference side, see DTO Infer and Generation.
Relationship to ORM and controller contracts
DTOs sit between backend data structure and backend API contracts.
That means DTO design should often be read together with:
Implementation checks for DTO changes
When creating DTOs:
- prefer reuse through mapped-class helpers when the shape is derived from existing classes
- keep DTO validation and OpenAPI concerns aligned through
@Api.field - decide whether the contract should be an explicit DTO class or an inferred DTO
- avoid re-declaring fields manually if Vona’s DTO-generation or class-derivation tools already solve the problem
- treat DTO design as part of the contract between backend handlers, models, and frontend integration
- choose explicit DTOs when named long-lived contracts matter, and inferred DTOs when the model/query shape already expresses the contract clearly