--- id: "SAAS-012" title: "Patrones Base CRUD" type: "Module" status: "Published" priority: "P2" module: "crud-base" version: "1.0.0" created_date: "2026-01-07" updated_date: "2026-01-10" --- # SAAS-012: Patrones Base CRUD ## Metadata | Campo | Valor | |-------|-------| | Codigo | SAAS-012 | | Modulo | crud-base | | Prioridad | P2 | | Estado | Documentacion | | Tipo | Guia de Patrones | | Ultima Actualizacion | 2026-01-10 | --- ## Descripcion Este documento describe los patrones CRUD utilizados en el proyecto template-saas. Actualmente cada modulo implementa su propia logica CRUD de forma independiente, siguiendo las convenciones descritas aqui. **Nota importante:** Las clases base documentadas (BaseCrudService, BaseCrudController) son patrones recomendados, no clases implementadas. Cada modulo implementa su propia logica siguiendo estos patrones. --- ## Patron Arquitectonico Actual ### Estructura por Modulo Cada modulo backend sigue esta estructura: ``` modules/[nombre]/ ├── [nombre].controller.ts # Endpoints REST ├── [nombre].module.ts # Configuracion NestJS ├── [nombre].service.ts # Logica de negocio (o services/*.service.ts) ├── dto/ │ ├── create-[nombre].dto.ts │ ├── update-[nombre].dto.ts │ └── [nombre]-response.dto.ts ├── entities/ │ └── [nombre].entity.ts # Entidad TypeORM └── __tests__/ └── [nombre].service.spec.ts ``` ### Ejemplo de Modulos que Siguen el Patron - `modules/storage/` - Patron mas limpio y recomendado - `modules/webhooks/` - Implementacion completa - `modules/tenants/` - Implementacion simple --- ## Campos Base Comunes Todas las entidades incluyen estos campos base: | Campo | Tipo | Descripcion | |-------|------|-------------| | id | UUID | Primary key, auto-generado | | tenant_id | UUID | Foreign key a tenants (RLS) | | created_at | TIMESTAMP | Fecha de creacion | | updated_at | TIMESTAMP | Fecha ultima modificacion | | deleted_at | TIMESTAMP | Soft delete (nullable) | | created_by | UUID | Usuario que creo (opcional) | | updated_by | UUID | Usuario que modifico (opcional) | --- ## Patron de Paginacion Todos los endpoints de listado soportan paginacion: ### Query Parameters ```typescript interface PaginationQuery { page?: number; // default: 1 limit?: number; // default: 10, max: 100 sort?: string; // campo a ordenar order?: 'ASC' | 'DESC'; // default: 'DESC' } ``` ### Respuesta Paginada ```typescript interface PaginatedResponse { data: T[]; meta: { page: number; limit: number; total: number; totalPages: number; }; } ``` --- ## Patron de Filtros ```typescript interface FilterQuery { search?: string; // busqueda full-text status?: string; // filtro por estado from?: string; // fecha inicio (ISO) to?: string; // fecha fin (ISO) } ``` --- ## Endpoints REST Estandar Cada modulo CRUD expone estos endpoints: | Metodo | Ruta | Descripcion | |--------|------|-------------| | GET | /[recurso] | Listar con paginacion | | GET | /[recurso]/:id | Obtener por ID | | POST | /[recurso] | Crear nuevo | | PATCH | /[recurso]/:id | Actualizar parcial | | DELETE | /[recurso]/:id | Eliminar (soft delete) | --- ## SQL Base para Tablas Template recomendado para nuevas tablas: ```sql CREATE TABLE schema_name.table_name ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES tenants.tenants(id), -- Campos especificos aqui -- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ ); -- Indices estandar CREATE INDEX idx_table_tenant ON schema_name.table_name(tenant_id); CREATE INDEX idx_table_deleted ON schema_name.table_name(deleted_at) WHERE deleted_at IS NULL; -- RLS ALTER TABLE schema_name.table_name ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation ON schema_name.table_name USING (tenant_id = auth.get_current_tenant()); -- Trigger updated_at CREATE TRIGGER set_updated_at BEFORE UPDATE ON schema_name.table_name FOR EACH ROW EXECUTE FUNCTION update_updated_at(); ``` --- ## Decoradores y Validacion ### Decoradores Utiles ```typescript // Excluir de respuesta @Exclude() deleted_at: Date; // Transformar fecha @Transform(({ value }) => value?.toISOString()) created_at: Date; // Validacion condicional @ValidateIf(o => o.type === 'special') @IsNotEmpty() specialField: string; ``` ### Interceptores ```typescript // Transformar respuesta @UseInterceptors(ClassSerializerInterceptor) // Log de audit @UseInterceptors(AuditInterceptor) ``` --- ## Roadmap: Clases Base Genericas ### Implementacion Futura Planificada Se planea implementar clases base reutilizables para reducir duplicacion: 1. **BaseEntity** - Campos comunes (id, timestamps, tenant_id) 2. **BaseCrudService** - Operaciones CRUD genericas con TypeORM 3. **BaseCrudController** - Endpoints REST estandar 4. **BaseDto** - Validaciones comunes **Estado:** Pendiente de implementacion **Prioridad:** Baja (cada modulo funciona correctamente de forma independiente) **Ubicacion propuesta:** `apps/backend/src/shared/` --- ## Referencias - Implementaciones en: `apps/backend/src/modules/*/` - Ejemplo recomendado: `modules/storage/` - DDL templates: `apps/database/ddl/schemas/` --- **Ultima actualizacion:** 2026-01-10