workspace/projects/gamilit/docs/95-guias-desarrollo/backend/DTO-CONVENTIONS.md
rckrdmrd ea1879f4ad feat: Initial workspace structure with multi-level Git configuration
- Configure workspace Git repository with comprehensive .gitignore
- Add Odoo as submodule for ERP reference code
- Include documentation: SETUP.md, GIT-STRUCTURE.md
- Add gitignore templates for projects (backend, frontend, database)
- Structure supports independent repos per project/subproject level

Workspace includes:
- core/ - Reusable patterns, modules, orchestration system
- projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.)
- knowledge-base/ - Reference code and patterns (includes Odoo submodule)
- devtools/ - Development tools and templates
- customers/ - Client implementations template

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 10:44:23 -06:00

438 lines
10 KiB
Markdown

# Convenciones de DTOs - Backend GAMILIT
**Fecha de creacion:** 2025-11-29
**Version:** 1.0
**Estado:** VIGENTE
**Contexto:** Estandarizacion de DTOs para eliminar duplicaciones
---
## 1. Principios Fundamentales
### 1.1 Proposito de los DTOs
- **Data Transfer Objects**: Transportar datos entre capas
- **Validacion**: Aplicar reglas de validacion con `class-validator`
- **Documentacion API**: Generar schemas Swagger con `@nestjs/swagger`
- **Transformacion**: Convertir/serializar datos con `class-transformer`
### 1.2 NO Duplicar
**Prohibido:**
- Crear DTOs con estructura identica a otros existentes
- Copiar campos entre DTOs sin usar herencia/composicion
**Permitido:**
- Extender DTOs base (`extends`)
- Usar utility types (`PartialType`, `OmitType`, `PickType`)
- Crear DTOs genericos reutilizables
---
## 2. DTOs Genericos Base
### 2.1 PaginatedResponseDto<T>
**Ubicacion:** `/shared/dto/common/paginated-response.dto.ts`
```typescript
import { ApiProperty } from '@nestjs/swagger';
export class PaginatedResponseDto<T> {
@ApiProperty({ description: 'Array de elementos', isArray: true })
data!: T[];
@ApiProperty({ description: 'Total de elementos', example: 100 })
total!: number;
@ApiProperty({ description: 'Pagina actual', example: 1 })
page!: number;
@ApiProperty({ description: 'Elementos por pagina', example: 10 })
limit!: number;
@ApiProperty({ description: 'Total de paginas', example: 10 })
total_pages!: number;
}
```
**Uso:**
```typescript
import { PaginatedResponseDto } from '@shared/dto/common';
// Extender para tipo especifico
export class PaginatedUsersDto extends PaginatedResponseDto<UserDetailsDto> {}
// O usar directamente en controller
@ApiOkResponse({ type: () => PaginatedResponseDto<UserDetailsDto> })
async getUsers(): Promise<PaginatedResponseDto<UserDetailsDto>> { ... }
```
### 2.2 BaseResponseDto
**Ubicacion:** `/shared/dto/common/base-response.dto.ts`
```typescript
import { ApiProperty } from '@nestjs/swagger';
import { Expose, Type } from 'class-transformer';
import { Exclude } from 'class-transformer';
@Exclude()
export class BaseResponseDto {
@Expose()
@ApiProperty({ description: 'ID unico', example: 'uuid-here' })
id!: string;
@Expose()
@Type(() => Date)
@ApiProperty({ description: 'Fecha de creacion' })
created_at!: Date;
@Expose()
@Type(() => Date)
@ApiProperty({ description: 'Fecha de actualizacion' })
updated_at!: Date;
}
```
**Uso:**
```typescript
import { BaseResponseDto } from '@shared/dto/common';
export class UserResponseDto extends BaseResponseDto {
@Expose()
@ApiProperty()
email!: string;
@Expose()
@ApiProperty()
username!: string;
}
```
---
## 3. Estructura de Carpetas
### 3.1 DTOs por Modulo
```
modules/[moduleName]/
└── dto/
├── index.ts # Barrel export
├── create-[entity].dto.ts # DTO para crear
├── update-[entity].dto.ts # DTO para actualizar
├── [entity]-response.dto.ts # DTO de respuesta
├── [entity]-filters.dto.ts # DTO para filtros/query
└── [subdominio]/ # Subdominios si hay muchos
├── index.ts
└── *.dto.ts
```
### 3.2 DTOs Compartidos
```
shared/
└── dto/
├── common/
│ ├── index.ts
│ ├── paginated-response.dto.ts
│ └── base-response.dto.ts
├── auth/
│ └── *.dto.ts
└── notifications/
└── *.dto.ts
```
---
## 4. Convenciones de Nombrado
### 4.1 Nombres de Archivos
| Tipo | Patron | Ejemplo |
|------|--------|---------|
| Crear | `create-[entity].dto.ts` | `create-user.dto.ts` |
| Actualizar | `update-[entity].dto.ts` | `update-user.dto.ts` |
| Respuesta | `[entity]-response.dto.ts` | `user-response.dto.ts` |
| Filtros | `[entity]-filters.dto.ts` | `user-filters.dto.ts` |
| Paginado | `paginated-[entities].dto.ts` | `paginated-users.dto.ts` |
| Listado | `list-[entities].dto.ts` | `list-users.dto.ts` |
### 4.2 Nombres de Clases
| Tipo | Patron | Ejemplo |
|------|--------|---------|
| Crear | `Create[Entity]Dto` | `CreateUserDto` |
| Actualizar | `Update[Entity]Dto` | `UpdateUserDto` |
| Respuesta | `[Entity]ResponseDto` | `UserResponseDto` |
| Detalles | `[Entity]DetailsDto` | `UserDetailsDto` |
### 4.3 Campos (snake_case para DB)
```typescript
// Campos que mapean a base de datos: snake_case
export class UserResponseDto {
user_id!: string; // snake_case (DB)
created_at!: Date; // snake_case (DB)
total_xp!: number; // snake_case (DB)
}
// Campos calculados o de UI: camelCase
export class UserFiltersDto {
searchQuery?: string; // camelCase (no DB)
sortOrder?: string; // camelCase (no DB)
}
```
---
## 5. Decoradores de Validacion
### 5.1 Decoradores Requeridos
```typescript
import {
IsString, IsEmail, IsNotEmpty, IsOptional,
IsUUID, IsEnum, IsBoolean, IsNumber,
MinLength, MaxLength, Min, Max,
IsArray, ArrayNotEmpty, ValidateNested
} from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
```
### 5.2 Patron Estandar
```typescript
export class CreateUserDto {
// Campo requerido
@ApiProperty({ description: 'Email del usuario', example: 'user@example.com' })
@IsEmail({}, { message: 'Formato de email invalido' })
@IsNotEmpty({ message: 'Email es requerido' })
email!: string;
// Campo opcional
@ApiPropertyOptional({ description: 'Nombre del usuario' })
@IsOptional()
@IsString()
@MaxLength(100)
name?: string;
// Campo enum
@ApiProperty({ enum: UserRoleEnum, description: 'Rol del usuario' })
@IsEnum(UserRoleEnum, { message: 'Rol invalido' })
role!: UserRoleEnum;
// Campo nested
@ApiProperty({ type: () => AddressDto })
@ValidateNested()
@Type(() => AddressDto)
address!: AddressDto;
}
```
### 5.3 Mensajes de Error
**Siempre incluir mensajes personalizados:**
```typescript
@IsNotEmpty({ message: 'El campo {property} es requerido' })
@MinLength(8, { message: 'La contrasena debe tener al menos 8 caracteres' })
@IsEnum(Status, { message: 'El estado debe ser uno de: {constraints}' })
```
---
## 6. Utility Types de NestJS
### 6.1 PartialType
```typescript
import { PartialType } from '@nestjs/mapped-types';
// Todos los campos de CreateUserDto se vuelven opcionales
export class UpdateUserDto extends PartialType(CreateUserDto) {}
```
### 6.2 OmitType
```typescript
import { OmitType } from '@nestjs/mapped-types';
// Excluye campos especificos
export class UpdatePasswordDto extends OmitType(CreateUserDto, ['email', 'role']) {}
```
### 6.3 PickType
```typescript
import { PickType } from '@nestjs/mapped-types';
// Solo incluye campos especificos
export class LoginDto extends PickType(CreateUserDto, ['email', 'password']) {}
```
### 6.4 Combinaciones
```typescript
// Combinar: Parcial sin password
export class UpdateUserDto extends PartialType(
OmitType(CreateUserDto, ['password'])
) {}
```
---
## 7. Serializacion con class-transformer
### 7.1 Expose/Exclude
```typescript
import { Exclude, Expose } from 'class-transformer';
@Exclude()
export class UserResponseDto {
@Expose()
id!: string;
@Expose()
email!: string;
// No expuesto, nunca se serializa
password!: string;
}
```
### 7.2 Transform
```typescript
import { Transform } from 'class-transformer';
export class UserResponseDto {
@Expose()
@Transform(({ value }) => value?.toLowerCase())
email!: string;
@Expose()
@Transform(({ value }) => new Date(value).toISOString())
created_at!: string;
}
```
---
## 8. Barrel Exports
### 8.1 index.ts de Modulo
```typescript
// modules/auth/dto/index.ts
export * from './create-user.dto';
export * from './update-user.dto';
export * from './user-response.dto';
export * from './login.dto';
```
### 8.2 index.ts de Shared
```typescript
// shared/dto/common/index.ts
export * from './paginated-response.dto';
export * from './base-response.dto';
```
---
## 9. Mapeo DTO <-> Entity <-> Frontend
### 9.1 Flujo de Datos
```
Frontend Request
[CreateUserDto] ← Validacion + Transformacion
[UserEntity] ← Logica de negocio
[UserResponseDto] ← Serializacion
Frontend Response
```
### 9.2 Sincronizacion con Frontend
Los DTOs de respuesta deben coincidir con los types del frontend:
```typescript
// Backend: UserResponseDto
export class UserResponseDto {
id!: string;
email!: string;
created_at!: Date;
}
// Frontend: User type (debe coincidir)
interface User {
id: string;
email: string;
created_at: string; // Date serializada
}
```
**Herramienta de sincronizacion:**
- `npm run generate:api-types` genera types desde OpenAPI
- Frontend debe usar `/generated/api-types.ts` como SSOT
---
## 10. Anti-patrones a Evitar
### 10.1 NO: DTOs Duplicados
```typescript
// MAL: 8 DTOs con misma estructura
class PaginatedUsersDto { data, total, page, limit, total_pages }
class PaginatedAlertsDto { data, total, page, limit, total_pages }
// ... 6 mas
// BIEN: Usar generico
class PaginatedUsersDto extends PaginatedResponseDto<UserDto> {}
```
### 10.2 NO: Validaciones Inconsistentes
```typescript
// MAL: Regex diferente en cada DTO
@Matches(/^[a-z]+$/) // en CreateUserDto
@Matches(/^[A-Za-z]+$/) // en UpdateUserDto
// BIEN: Constante compartida
const USERNAME_REGEX = /^[a-zA-Z0-9_]+$/;
@Matches(USERNAME_REGEX)
```
### 10.3 NO: Campos sin Decoradores
```typescript
// MAL: Sin decoradores
export class CreateUserDto {
email: string; // Sin validacion
}
// BIEN: Siempre decorar
export class CreateUserDto {
@ApiProperty()
@IsEmail()
@IsNotEmpty()
email!: string;
}
```
---
## 11. Checklist de Creacion de DTO
Antes de crear un nuevo DTO:
1. [ ] Verificar que no existe DTO similar
2. [ ] Determinar si puede extender DTO base
3. [ ] Usar nombres segun convenciones
4. [ ] Agregar todos los decoradores necesarios
5. [ ] Incluir mensajes de error personalizados
6. [ ] Exportar en barrel file (index.ts)
7. [ ] Documentar con JSDoc si es complejo
8. [ ] Actualizar BACKEND_INVENTORY.yml
---
## 12. Referencias
- **Tipos Frontend:** `docs/95-guias-desarrollo/frontend/TYPES-CONVENTIONS.md`
- **Naming API:** `docs/95-guias-desarrollo/backend/NAMING-CONVENTIONS-API.md`
- **class-validator:** https://github.com/typestack/class-validator
- **class-transformer:** https://github.com/typestack/class-transformer
- **@nestjs/mapped-types:** https://docs.nestjs.com/openapi/mapped-types
---
## 13. Changelog
| Version | Fecha | Cambios |
|---------|-------|---------|
| 1.0 | 2025-11-29 | Creacion inicial - Plan de estandarizacion |