Módulos de feature
Las features nuevas del backend no se agregan al árbol plano de controllers/v1 +
services/v1: van como módulo autocontenido en src/modules/<nombre>/.
La forma
src/modules/<nombre>/
├── module.ts # singleton: initialize / getControllers / getEntities / getServices
├── module.config.ts # el objeto ModuleConfig (name, version, controllers, entities, providers)
├── index.ts # re-exporta todo lo público del módulo
├── controllers/ services/ entities/ dto/
├── interfaces/ constants/ providers/ # opcionales
└── MODULE.md # opcional pero recomendado
module.config.ts es el "decorador @Module" escrito a mano:
export const STOCKS_MODULE_CONFIG: ModuleConfig = {
name: "StocksModule",
version: "1.0.0",
description: "Module for managing supplies, stock movements, and warehouse inventory",
controllers: [SupplyController],
entities: [Supply, SupplyMovement, SupplyMovementDetail, SupplyNote],
providers: [SupplyService, SupplyServiceV2, /* … */],
};
Los tres enganches
Si falta uno, el módulo no existe:
src/modules/index.ts→export { default as XModule } from "./x/module";src/AppConfig.ts→await XModule.initialize();src/controllers/v1/ControllerImportsV1.ts→...XModule.getControllers()
Las entidades entran solas al DataSource por el glob src/modules/**/entities/*.ts.
Cómo se importa
// ✅ desde fuera del módulo
import { SupplyService, Supply, CreateSupplyDTO } from "@modules/stocks";
// ✅ desde dentro del módulo
import { SupplyService } from "./services";
// ❌ para uso normal: pierde tipado y autocompletado
const services = StocksModule.getServices();
getServices() y getMetadata() son para tests, debugging e introspección, no para resolver
dependencias.
initialize() desde código de producciónAppConfig ya lo hace en el bootstrap. Solo tiene sentido llamarlo en tests (beforeAll) y en
scripts standalone que no pasan por AppConfig.
Los cinco módulos que existen
stocks — inventario
El más grande y el que más se toca.
- Entidades (viven en la BD
finances):Supply,SupplyMovement,SupplyMovementDetail,SupplyMovementHistory,SupplyReservation,SupplyNote,SupplyNoteDetails. - Servicios:
SupplyService(v1) yservices/v2/SupplyService.ts(~3k líneas, el vigente para lo nuevo),SupplyMovementService,SupplyNoteService. - Controller:
SupplyController→/v1/supply. - Tiene su propio
MODULE.mdcon la guía de imports.
Lo que mueve stock de verdad no está acá sino en SQL: ver Stock y transferencias.
notifications — notificaciones in-app y push
Notifications (in-app, por usuario) y MobileNotifications (un token de Expo por dispositivo).
Ver Notificaciones y push.
whatsapp — mensajería con el paciente
Patrón provider: IWhatsAppProvider → MetaWhatsAppProvider, elegido por un factory. Suma
WhatsAppTemplateManager, WhatsAppWorker y un validador de firma del webhook.
Ver WhatsApp.
repairshop — talleres externos
Un CRUD chico pero importante en el dominio: un taller es un destino válido de un Supply.
Buen molde para copiar cuando arrancás un módulo nuevo.
mobileConfig — versionado de BullStock
Una fila por release de la app (versión semver, entorno, URL del build, changelog). La app la
consulta al arrancar para decidir si obliga a actualizar. En dev la app no consulta el backend:
lee de su .env.
Lo que NO es módulo
Autorizaciones, remitos, hojas de ruta, pacientes, vehículos y reportes siguen en el árbol plano
controllers/v1 + services/v1|v2|v3. Son el grueso del sistema y no están migrados; no
intentes moverlos "de paso" mientras hacés otra cosa.