Dar de baja a un tenant entero
Cerrar una cuenta tiene una pregunta fácil: los datos son de esa persona, y se van con ella. Cerrar un tenant no. Sus filas describen cosas que siguen existiendo cuando él ya no está —personas que siguen siendo clientes de alguien, un historial que alguien tiene que poder enseñar— y por eso no hay un solo destino.
La baja no es un
DELETE FROM … WHERE tenantId = …. Es un reparto: cada dato va a quien pueda seguir respondiendo de él, y solo se suprime lo que no reclama nadie.
Los cuatro destinos
| Destino | Qué va ahí | Por qué |
|---|---|---|
| Sigue donde está | Lo que el tenant escribió sobre algo que hoy atiende otro | Nadie tiene que moverlo: si la lectura es por el objeto y no por el tenant, el nuevo ya lo ve |
| Tenant de sistema | Los clientes que seguían con él, con lo suyo | Tienen contrato contigo, no con él: la cuenta sigue funcionando |
| Bloqueado | Lo que no reclama nadie pero todavía no puede destruirse | Hay un plazo legal o de prescripción que corre aunque el tenant se haya ido |
| Suprimido | Sus datos propios, sus cuentas de personal, lo pendiente | No sirven a nadie más |
Lo que decide el destino de cada fila es de quién es hoy el objeto, no quién escribió la fila. En petid-api eso es la mascota: un informe de la clínica que se va, sobre un paciente que hoy atiende otra, se queda; el mismo informe sobre un paciente sin dueño, se bloquea.
Lo que no hay que mover
La parte más barata del reparto es la que no se programa. Si el historial se lee por el objeto
—findByPetId, sin filtrar por tenant— la cesión ya está hecha: en cuanto el cliente se traslada,
el nuevo tenant ve lo que escribió el anterior, con su autoría intacta.
Eso convierte una consulta en una regla de negocio, así que conviene fijarla con un test: “una mascota que cambia de clínica sigue viendo los informes de la clínica inactiva”. El día que alguien añada un filtro por tenant activo, la cesión se rompe sin que falle nada más.
El tenant de sistema
Un tenant con id fijo, creado por migración, donde caen los clientes que se quedaron sin el suyo. No es el tenant del superadmin: ese es de la plataforma y tiene personal; este no tiene ninguno y no se desactiva nunca.
Tiene un precio que conviene decir en voz alta: esos datos pasan a ser tuyos. Mientras había un tenant, tú eras el encargado; después eres el responsable, y eso se sostiene en el contrato que tienes con esa persona, no en el que tenías con quien se fue.
Bloquear no es borrar ni conservar
Un dato bloqueado existe, no lo ve nadie y tiene fecha de caducidad. Las tres cosas a la vez:
- sin fecha, “bloqueado” es quedárselo para siempre con otro nombre;
- sin ocultarlo, no es un bloqueo;
- sin conservarlo, se incumple el plazo que obligaba a guardarlo.
La fecha se calcula por objeto, desde su último registro, no desde la baja: dos pacientes del mismo tenant vencen en momentos distintos porque su última visita fue en momentos distintos. Un cron recoge lo vencido y ahí sí destruye: filas y ficheros.
Anonimizar conservando la autoría
El tenant y su personal se anonimizan, pero no del todo: sobrevive lo que firma lo que se queda. El nombre comercial del tenant, y el nombre y número de colegiado de quien firmó un informe que sigue vivo, son parte del acto, no datos de contacto.
Tiene una consecuencia en las pantallas: si el repositorio esconde los tenants borrados, el historial cedido dirá que no lo escribió nadie. Hace falta una consulta que los incluya, y es fácil de olvidar hasta que se ve el hueco.
Las claves ajenas hacen el trabajo difícil
La pregunta incómoda —¿ya puedo borrar esta fila?— la contesta la base de datos. Con Restrict, un
borrado falla mientras algo apunte a la fila, así que el cron de limpieza puede ser tonto a
propósito:
para cada tenant o usuario anonimizado:
intenta borrarlo
si falla, déjalo: algo suyo sigue vivo
Contarlo al revés —calcular quién ya no es referenciado— es reimplementar la integridad referencial en el lenguaje equivocado. Y contar con que el borrado en cascada resuelva el reparto es peor: una cascada se lleva por delante justo las filas que otro tenant necesitaba.
El orden importa
Dentro de la transacción, primero mover y luego bloquear lo que queda: así “lo que queda” se define solo, sin listas de exclusión. Fuera de la transacción van los ficheros y lo que vive en otros servicios, que no pueden participar en ella; si eso falla, la baja no se deshace, se apunta y se reintenta.
Y antes de todo, cortar las sesiones del personal: quien esté dentro mientras se reparte vería un panel a medias. Las de los clientes no, que se van a otro sitio y siguen usando su app.
Qué probar
- Un cliente que se queda → acaba en el tenant de sistema, puede iniciar sesión y ve lo suyo.
- Un objeto sin dueño con historial de otro tenant → sobrevive al vencer su bloqueo; solo desaparece lo que escribió el que se fue.
- El personal → no puede entrar, y el que firmó conserva su nombre.
- La limpieza → no borra el tenant mientras siga siendo la autoría de algo.