Skip to content

Modulo arquitectura multitenant

Asi se tenia pensando en un inicio manejar la aplicación y la gestión de cliente

Arquitectura original

  • i. Suponiendo que tengo dos usuarios registrados. Jeimy & Neider debe existir la opción de poder establecer por ejemplo que el usuario Neider es para Piscinas y el usuario Jeimy es para Plantas de tratamiento y al hacer esto yo cuando entre a la aplicación Neider vea piscinas y Jeimy plantas de tratamiento.
  • ii. Dentro de cada empresa van a haber muchos usuarios pero cada empresa va a tener un objetivo sea Piscinas o Plantas de tratamiento pero van a ser un objetivo diferente
  • iii. El usuario que asigne si es Piscinas o Plantas de tratamiento somos nosotros Tree-a que es el SuperUsuario : llego un nuevo cliente yo diligencio la información de la empresa (Registro la empresa) y le asigno si es Piscinas o Plantas de tratamiento de acuerdo a esa asignación es que el va a ver esos módulos en la parte izquierda (Menú) si se asigno piscinas el cliente va a ver los módulos que tenemos a la fecha Miercoles 22 noviembre 2023 y lo mismo con plantas de tratamiento y si por alguna razon yo tengo un clientes que tiene los dos Piscinas o Plantas de tratamiento el cliente puede ver los dos paquetes de modulos.
  • IV. Debemos estar en la capacidad de que cuando se cree una empresa y se le creen usuarios yo pueda decirle si es Piscinas o Plantas de tratamiento
  • V. Cada empresa tenga su información independiente ejemplo creamos la empresa tree-a dentro de esta empresa esta el usuario administrador - Neider - El usuario Neider se encarga de crear los demas usuarios que tenga en su compañia con rol de gestor o de administrador y se le asigna piscinas... cuando le pasemos el link con su usuario y contraseña el inicia y ve que tiene sus modulos, tengo la opción de crer más usuarios para mi empresa y lo que el cree en esa empresa, los usuarios solo van a acceder a la información de esa empresa porque se crea como una rama independiente, que si bien tiene parte del mismo tallo es una rama que va a tener un grupo de datos independientes...
  • VI. Hay que crear un módulo de "Gestión de empresas",o "Gestión de usuarios" que solo este activo para el Super administrador - ESTE DEBE SER EL PUNTO DE PARTIDA
  • VII. Una vez yo creo una empresa le pongo nombre,nit,direccion,etc. y se le Crea un usuario administrador y se le asigna si Piscinas o Plantas de tratamiento una vez se haga esto le doy guardar debo tener una vista donde pueda ver todas las Empresas con el crud completo pueda ver el detalle para saber quien es, que contraseña se le asigno que usuario se le asigno por si la persona pierde su contraseña. A esa persona le pasamos el link - Usuario y contraseña ingresa y ve la vista que tenemos actualmente...
    ** Resumen Se va a crear un módulo nuevo desde donde se crea la empresa y le asigno un usuario...

Caso hipotetico

  • Hay un cliente interesado en 7 plantas de tratamiento. Este cliente tiene 7 subclientes y de cierta forma quiere tener control sobre ellos. Su rol es de superAdmin, es una especie de distribuidor.Este cliente tiene una especie de dashboard con el listado de plantas de tratamiento.(sus 7 clientes) al dar click en una planta de tratamiento sera redireccionado a link de esa planta (que es independiente del resto), debe aparecer un login para que inicie sesión como superadmin...una vez logueado,ve un listado de reportes, graficas, para esto podemos usar MONGO CHARTS - Y EMBEBER LAS GRÁFICAS...

Arquitectura multi-tenant y restricción a los endpoints de acuerdo al rol + aislamiento de datos de acuerdo al cliente

  • tenantMiddleware: Es el middleware que implementa la arquitectura multi-tenant conectando cada usuario a su DB correspondiente
  • permissionsMatrix.ts: Su objetivo es:Definir qué funcionalidades puede acceder cada tipo de usuario en el sistema
  • functionalities-FUNCTIONALITIES: el objetivo de FUNCTIONALITIES es: centralizar y tipar todas las funcionalidades del sistema como constantes para evitar errores de escritura y facilitar el mantenimiento del codigo. Define constantes para cada funcionalidad (ej: CHEMICAL_INVENTORY_CREATION, USER_CREATION).
  • functionalities-ROLE_FUNCTIONALITIES: Su objetivo es documentar y referenciar a que funcionalidades tiene acceso cada rol en el sistema, sirviendo como guía de permisos paradesarrolladores. Organiza las funcionalidades por rol: TREEA_ADMIN, ADMIN_COMPANY, SUPERVISOR, OPERADOR,VISOR En resumen: FUNCTIONALITIES = constantes tipadas, ROLE_FUNCTIONALITIES = documentación de permisos por rol.
  • userAdminCompanyController: El objetivo de este archivo es gestionar las operaciones CRUD de usuarios SUPERVISOR y OPERADOR que pertenecen a empresas cliente específicas, permitiendo que los administradores de empresa (ADMIN_COMPANY) y administradores de TREEA (TREEA_ADMIN) creen, consulten, actualicen y activen/desactiven usuarios en las bases de datos específicas de cada cliente (Importante aclarar que todo los usuarios se guardan en la db principal - treea-plantas).Este controller tiene una caracteristica esencial, estamos usando multi-tenant es decir nos conectamos a la db especifica de cada cliente
  • src/db/getModel.ts: El objetivo de este archivo es proporcionar una función utilitaria que obtiene o crea dinámicamente modelos de Mongoose para una conexión específica, facilitando el trabajo con arquitecturas multitenant donde cada cliente tiene su propia base de datos. Esta funcion es super importante la utilizamos en muchos lugares del codigo, en resumen obtiene o crea modelos para una conexion ya establecida
  • src/db/tenantConnection.ts: El objetivo de este archivo es gestionar y cachear conexiones a bases de datos específicas de cada cliente (tenant) para optimizar el rendimiento y evitar crear múltiples conexiones innecesarias al mismo cliente.Verifica si ya existe una conexión para el clientId en el Map de conexiones. Si existe: Retorna la conexión cacheada (reutilización). Si no existe Obtiene la URL de conexión específica del cliente (showUrlBase) y crear una nueva conexión a la db. En resumen: Nos permite conectar a la db especifica de cada cliente (tenant), de forma dinamica y optimizada
  • src/middlewares/tenant.ts (tenantMiddleware) El objetivo de este archivo es proporcionar un middleware que establece automáticamente la conexión a la base de datos específica del cliente (tenant) para cada petición HTTP, basándose en el clientId del usuario autenticado.Establece la conexión del tenant: Usa getTenantConnection para obtener la conexión específica del cliente.Adjunta la conexión a la request: Guarda req.dbConnection para uso posterior en los controladores. En resumen: Este middleware es esencial para la arquitectura multitenant, ya que automatiza la selección de la base de datos correcta para cada petición.
  • src/middlewares/validatePermissions.ts (validateFunctionalityPermission): El objetivo de este archivo es proporcionar un middleware que valida si un usuario autenticado tiene permisos para acceder a una funcionalidad especifica del sistema, utilizando la matriz de permisos definida en permissionsMatrix.ts. Este archivo hace lo siguiente:
    • Extrae el usuario del token JWT: Obtiene la informacion del usuario desde req.user (token decodificado)
    • Valida que el usuario exista: Verifica que el usuario este autenticado y presente
    • Verifica el estado del usuario: Confirma que el usuario este activo (state === 'active')
    • Valida permisos: Usa hasPermission(user.role, functionality) para verificar si el rol del usuario tiene acceso a la funcionalidad solicitada
    • Adjunta usuario a la request: Guarda req.currentUser para uso posterior en los controladores Este middleware es fundamental para el sistema de seguridad, ya que protege cada endpoint basandose en los permisos definidos en la matriz de permisos.
  • src/routes/v1/userAdminCompany.ts: Contiene toda las rutas para gestionar los usuarios propios de cada cliente

  • auth - login: Se agrego clientId en req.body y realizamos una conexion dinamica a la db del cliente en caso de aplicar sino usamos la db principal, por ultimo a la hora de generar el JWT, tambien pasamos el clientId porque lo necesitamos para realizar validaciones
  • userClientController.ts: El objetivo de este archivo es gestionar las operaciones CRUD de usuarios que pertenecen a clientes especificos, conectandose directamente a las bases de datos de los clientes para crear, consultar, actualizar y activar/desactivar usuarios ADMIN_COMPANY, SUPERVISOR y OPERADOR.
  • generateClientDbEnvVar: Sirve para generar el nombre de la variable de entorno de la base de datos del cliente basandose en el nombre de la empresa, NIT y fecha.Ejemplo: DB_CLIENT_ECOVITAL_123456_20240801
  • showUrlBase: Sirve para obtener la URL de conexion a la base de datos especifica de un cliente usando su ID. En resumen: generateClientDbEnvVar crea el nombre de la variable de entorno, showUrlBase la usa para obtener la URL de conexion real a la DB del cliente. generateJWT: Se agrego el parametro clientId a la funcion generateJWT para incluir el ID del cliente en el token. El token ahora contiene informacion del cliente para validaciones multitenant. hasTransaction.ts - updateTransaction: se agrega
  • IUserModel: se agregan las propiedades company, clientId y createdBy esta ultima para identificar quien creo al usuario. Se debe conservar solo company temporalmente se dejan ambas
  • ICustomRequest: Se agregan dos propiedad opcionales adminCompany y currentUser - posiblemente las eliminemos mas adelante
  • src/middlewares/validateJwt.ts: Se agrego soporte para clientId en el token JWT para validar usuarios de empresas cliente. Se extrae clientId del token junto con su id. Se asigna clientId al usuario para uso posterior. En resumen: Se agrego soporte para clientId en el token JWT para validar usuarios de empresas cliente. Se extrae clientId del token junto con su id. Se asigna clientId al usuario para uso posterior