Como funciona realmente un modulo dentro del CRM
Esta pagina explica la parte de codigo que faltaba: que archivos tiene un modulo, para que sirve cada uno y en que orden participa cuando el sistema arranca o cuando se despliega una DLL.
Estructura minima de un modulo
Modules/MIMODULO
Controllers/
Data/
Configurations/
Migrations/
MIMODULODbContext.cs
Entities/
Hooks/
Seed/
Services/
Views/
ModuleDescriptor.cs
ModuleInitializer.csNo todos los modulos necesitan todas las carpetas, pero esa es la estructura recomendada para un modulo funcional completo.
ModuleDescriptor: para que sirve
ModuleDescriptor describe el modulo. Es metadato, no logica de negocio.
- Identificador del modulo
- Nombre visible
- Version
- Dependencias requeridas
DbContextTypesi el modulo tiene base de datos- Si tiene seeds o no
public static class ModuleDescriptor
{
public static ModuleMetadata Metadata => new ModuleMetadata(
id: "PROVEEDORES",
name: "PROVEEDORES",
version: "1.0.0",
requiredModules: new[] { "ENTIDADES" },
optionalModules: Array.Empty<string>(),
dbContextType: typeof(PROVEEDORESDbContext)
);
}ModuleInitializer: para que sirve
ModuleInitializer registra servicios del modulo en el contenedor DI.
- Servicios del modulo
- Hooks del modulo
- Seeds del modulo si hace falta
public static class ModuleInitializer
{
public static void Register(IServiceCollection services)
{
services.AddScoped<IProveedorService, ProveedorService>();
services.AddScoped<IClienteAccionHook, ProveedorClienteAccionHook>();
services.AddScoped<IClienteTabHook, ProveedorClienteTabHook>();
}
}Que pasa al arrancar la aplicacion
- El cargador descubre modulos.
- Lee su
ModuleDescriptor. - Registra su
DbContextsi existe. - Ejecuta
ModuleInitializer.Register(...). - El gestor de migraciones intenta aplicar esquema del modulo.
- Despues se ejecutan seeds de datos base.
Que pasa al subir una DLL desde ADMINPANEL
- La DLL se despliega en
ModulosCompilados. - Se valida que tenga
ModuleDescriptoroModuleInitializer. - El modulo queda preparado en runtime.
- Para registro completo de servicios, rutas MVC, migraciones y seeds se requiere reiniciar la aplicacion.
DbContext del modulo
El DbContext del modulo es dueno de sus tablas.
public class MIMODULODbContext : DbContext
{
public DbSet<MiEntidad> MiEntidades => Set<MiEntidad>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(typeof(MIMODULODbContext).Assembly);
}
}Services del modulo
La logica de negocio no debe estar metida en controller ni en hook. Debe vivir en servicios del modulo.
- CRUD principal
- Validaciones del modulo
- Casos de uso
- Reglas de negocio
Controllers del modulo
Los controllers exponen la UI o la API del modulo.
- Paginas MVC del modulo
- Endpoints AJAX
- Acciones de detalle, index, crear, editar
No deben ser el sitio principal de la logica compleja.
Seeds del modulo
El seed debe usarse para bootstrap de datos, no como sustituto permanente de migraciones.
- Registrar modulo
- Crear permisos
- Sembrar menu
- Sembrar catalogos base
Permisos del modulo
Un modulo debe declarar permisos coherentes y usarlos de forma consistente.
new { Key = "PROVEEDORES.Ver", Nombre = "Ver proveedores" }
new { Key = "PROVEEDORES.Editar", Nombre = "Editar proveedores" }La misma clave debe coincidir en seed, atributo de controller y servicios de autorizacion.
Menu del modulo
El modulo puede exponer menu de dos formas:
- definicion sincronizada en BD
- atributos tipo
MenuEntrycomo fuente tecnica
La idea buena es: codigo como definicion base, BD como personalizacion.
Limites de modulo
Un modulo puede necesitar limites funcionales por plan o por empresa. Ejemplos:
- maximo de almacenes
- maximo de usuarios del modulo
- maximo de documentos al mes
- si una opcion premium esta activa o no
Como debe pensarse
- El modulo define que limites existen.
- El sistema core o admin guarda esos limites por plan o empresa.
- El servicio del modulo consulta el limite antes de ejecutar la accion.
Configuracion de modulo
Algunos modulos necesitan opciones configurables. Ejemplos:
- serie por defecto
- almacen por defecto
- activar o no una funcionalidad
- modo de trabajo del modulo
Regla recomendada
- la configuracion global del sistema vive en tablas core
- la configuracion funcional del modulo debe modelarse como opcion o tabla del modulo
- el modulo debe exponer su servicio de configuracion
public interface IModuloConfiguracionService
{
Task<string?> GetValorAsync(string moduloKey, int empresaId, string clave);
Task SetValorAsync(string moduloKey, int empresaId, string clave, string? valor);
}Opciones del modulo
Una opcion es una capacidad concreta activable o parametrizable dentro del modulo. Ejemplos:
- usar lotes
- usar tallas y colores
- usar doble aprobacion
- activar documentos avanzados
Las opciones suelen combinarse con:
- permisos
- limites
- configuracion por empresa o plan
Hooks del modulo
Si un modulo necesita integrarse en otro host, crea hooks.
- Tabs en fichas
- Acciones extra
- Inicializacion por evento, por ejemplo alta de empresa
services.AddScoped<IClienteTabHook, ProveedorClienteTabHook>(); services.AddScoped<IClienteAccionHook, ProveedorClienteAccionHook>();
Cuando un modulo depende de otro
Si un modulo necesita otro maestro, debe declararlo como dependencia.
- PROVEEDORES depende de ENTIDADES
- COMPRAS dependera de PROVEEDORES y PRODUCTOS
Resumen del ciclo de vida
1. Crear entidades 2. Crear configuraciones EF 3. Crear DbContext 4. Declarar ModuleDescriptor 5. Registrar servicios en ModuleInitializer 6. Crear migracion 7. Crear seed de datos base 8. Anadir controllers y vistas 9. Anadir hooks si el modulo extiende otra pantalla 10. Verificar permisos, menu y esquema