resources/views/pages/frontend/docs/arquitectura-usuarios.md
Última modificación: 24 Oct, 2026

Modelo de Identidad & Perfil EAV

La administración de identidades en Mañuti descarta la estructura genérica users provista por defecto en Laravel para adoptar una arquitectura empresarial adaptada a requerimientos corporativos, anonimización ISO 27701 y extensibilidad vertical mediante el patrón EAV (Entity-Attribute-Value).


1. Esquema Relacional de la Tabla usuarios

La tabla almacena las credenciales nucleares y los datos de contacto indispensables:

CREATE TABLE `usuarios` (
    `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    `usuario` VARCHAR(255) NOT NULL UNIQUE,
    `nombres` VARCHAR(255) NOT NULL,
    `apellido_paterno` VARCHAR(255) NOT NULL,
    `apellido_materno` VARCHAR(255) NULL,
    `correo` VARCHAR(255) NOT NULL UNIQUE,
    `email_verified_at` TIMESTAMP NULL,
    `password` VARCHAR(255) NOT NULL,
    `remember_token` VARCHAR(100) NULL,
    `cat_status` BIGINT UNSIGNED NOT NULL,
    `password_changed_at` TIMESTAMP NULL,
    `must_change_password` TINYINT(1) DEFAULT 0,
    `two_factor_secret` TEXT NULL,
    `two_factor_recovery_codes` TEXT NULL,
    `two_factor_confirmed_at` TIMESTAMP NULL,
    `created_at` TIMESTAMP NULL,
    `updated_at` TIMESTAMP NULL,
    `deleted_at` TIMESTAMP NULL,
    FOREIGN KEY (`cat_status`) REFERENCES `catalogos`(`id`)
);

Configuración del Proveedor en config/auth.php

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model'  => App\Models\Usuario::class,
    ],
],

2. Patrón EAV con Control de Versiones (configuracion_usuarios)

Para almacenar atributos personales (teléfono móvil, fecha de nacimiento, avatares, zona horaria, tema de UI) sin alterar el esquema relacional con migraciones constantes, Mañuti implementa la tabla configuracion_usuarios.

flowchart LR
    U["[usuarios]"]
    CONF["[configuracion_usuarios]"]
    CAT["[catalogos]<br/>(CFG_ACTIVO / CFG_HISTORICO)"]

    U <-->|"1 : N"| CONF
    CONF <-->|"N : 1"| CAT
CREATE TABLE `configuracion_usuarios` (
    `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    `usuario_id` BIGINT UNSIGNED NOT NULL,
    `clave` VARCHAR(255) NOT NULL,
    `valor` LONGTEXT NULL,
    `cat_status` BIGINT UNSIGNED NOT NULL,
    `created_at` TIMESTAMP NULL,
    `updated_at` TIMESTAMP NULL,
    `deleted_at` TIMESTAMP NULL,
    FOREIGN KEY (`usuario_id`) REFERENCES `usuarios`(`id`) ON DELETE CASCADE,
    FOREIGN KEY (`cat_status`) REFERENCES `catalogos`(`id`)
);

CREATE INDEX `config_usuario_clave_idx` ON `configuracion_usuarios`(`usuario_id`, `clave`);

3. Versionado de Historial e Inmutabilidad de Cambios

Al invocar $user->setConfig('clave', $valor), el sistema no ejecuta un UPDATE destructivo. Mantiene una auditoría histórica de cómo evoluciona el perfil:

flowchart TD
    INPUT["[Nuevo Valor: '55-9999-8888']"]
    CHECK{"¿Existe registro previo con<br/>cat_status = CFG_ACTIVO?"}
    UPDATE["1. Actualiza registro previo a<br/>cat_status = CFG_HISTORICO"]
    INSERT["2. Inserta nuevo registro con<br/>cat_status = CFG_ACTIVO"]

    INPUT --> CHECK
    CHECK -->|"Sí"| UPDATE
    UPDATE --> INSERT
    CHECK -->|"No"| INSERT
// app/Models/Usuario.php
public function setConfig(string $clave, mixed $valor): void
{
    $idActivo = Catalogo::where('catalogo', 'CFG_ACTIVO')->value('id');
    $idHistorico = Catalogo::where('catalogo', 'CFG_HISTORICO')->value('id');

    $valorFinal = $this->isEncryptedKey($clave) && !empty($valor)
        ? Crypt::encryptString($valor)
        : $valor;

    $actual = $this->configuraciones()
        ->where('clave', $clave)
        ->where('cat_status', $idActivo)
        ->first();

    if ($actual) {
        if ($actual->valor === $valorFinal) return;
        $actual->update(['cat_status' => $idHistorico]);
    }

    $this->configuraciones()->create([
        'clave'      => $clave,
        'valor'      => $valorFinal,
        'cat_status' => $idActivo
    ]);
}

4. Desencriptación Selectiva al Vuelo

El método $user->getConfig($clave) detecta dinámicamente si el atributo solicitado forma parte del arreglo de datos personales protegidos (security_pii_fields) y ejecuta el descifrado simétrico AES-256 en memoria RAM de manera transparente.


Enlaces Relacionados

Edita este documento en >> arquitectura-usuarios.md
← Volver a la Landing Page