Una de las cuestionas más relevantes al momento de diseñar muestro modelo de bases de datos es definir la llave primaria, sin embargo, esta acción casi siempre ovedece más a una necesidad de integridad que a un identificador real para el negocio.

Solo por poner algunos ejemplos, si creamos un tabla para libros, seguramente pondremos una columna autoincrementable llamada id o libro_id, cuando en realidad todos sabemos que el identificador del mundo real de un libro es el ISBN, del mismo modo, si creamos una tabla para guardar a las personas, vamos a crear una tabla similar con el id o persona_id, cuando en realidad el identificado sea el número de identificación de cada país, llamese CURP o DNI según cada país.

Si bien la estrategia de poner un identificado númerico ayuda mucho a consultar con mayor velocidad los registros y hacerlo de forma más uniforme, la realidad es que en el múndo moderno donde vivimos, muchos de los registros se buscan por su identificador del mundo real, es decir, lo que conocemos como Natural ID o Identificador Natural.

Por ejemplo, si estas leyendo este artículo, podrás observar que la URL de esta página sea algo así como https://codmind.com/blog/table-id-vs-natural-id, lo que quiere decir que en realidad el artículo está siendo buscado por su nombre (los guinos medios son para hacerlo amigables para el naveador) y no por un ID.

Otro ejemplo muy utilizado son las API, por ejemplo, imagina que estas desarrollando un servicio para crear un cobro recurrente a los usuario. Veamos como se vería un request con los IDs tradicionales:

{
  "moneda": 10,
  "precio": 199,
  "pais": 3,
  "status": 1,
  "frecuencia": 1
}

El request anterior, en realidad requiere de varias entidades externas, como es la moneda, el país, el status y la frecuencia de cobro, en este sentido, podrías decir que moneda=10 sería como decir moneda=USD, pais=3 sería US, status=1 sería activo y frecuencia=1 sería mensual. En este sentido, accediendo a la información mediante el id de la tabla y no mediante el Natural ID, lo cual haría la vida más simple para los usuario de nuestra API, la cual se podría ver de esta otra forma:

{
  "moneda": "usd",
  "precio": 199,
  "pais": "MX",
  "status": "activo",
  "frecuencia": "mensual"
}

Quiero que observer detenidamente estos dos request y cual crees que tenga más sentido para un usuario ageno a la aplicación y te darás cuenta que el natural id comienza a cobrar sentido.

¿Cómo se implementan un natural id?

La realidad es que el ID número que ya conocemos no esta peleado con el natural id, al contrario, pueden coexitir en una misma tabla, de esta forma, tenemos tablas que tiene el ID de siempre y una columna que puede funsionar con natural id. Si bien esta columna se puede llamar como sea, ya que dependera de cada tabla, la idea es que debe estar marcada como única, para impedir que existe otro registro con el mismo natural id, de la misma forma, deberemos crear un índice que  ayude a realizar búsquedas más rápida sobre ese campo.

Como podemos apreciar en el diagrama anterior, las tablas  Monedas, Status , Paises y Frecuencias cuanta con una columna que funciona como ID númerico y tenemos adicional una columna nombre que funciona como natural id. Esta útima columna, podrás apreciar que esta dentro de los índices marcada como única, lo que garantiza que cuando yo busque un registro por natural id siempre obtenga 0 o 1 registro, pero nunca más de 1.

Si vemos esto a nivel de aplicación, podríamos tener servicios que busquen los registros por su natural id, algo así:

public void suscripcion(SuscripcionRequest req) {
	
    Moneda moneda = monedaService.findByNombre(req.getMoneda())
    	.orElseThrow(() -> new RuntimeException("No existe la moneda" ));
        
    Pais pais = paisService.findByNombre(req.getPais())
    	.orElseThrow(() -> new RuntimeException("No existe el país" ));
    
    Frecuencia frecuencia = frecuenciaService.findByNombre(req.getFrecuencia())
    	.orElseThrow(() -> new RuntimeException("No existe la frecuencia" ));
        
    Status status = statusService.findByNombre(req.getStatus())
    	.orElseThrow(() -> new RuntimeException("No existe el status" ));
        
    Suscripcion suscripcion = Suscripcion.build
    	.moneda(moneda)
        .pais(pais)
        .frecuencia(frecuencia)
        .status(status)
        .precio(req.getPrecio())
        .build();
        
    suscripcionService.save(suscripcion);

}

Podrás apreciar que la capa de controladores busca las relaciones por natural id y luego la asigna al objeto suscripción para terminar persistiendola.

Conclusiónes

Como podemos observar implementar un natural id no implica nada adicional de lo que ya hacemos, ya que por lo general muchas de las tablas ya cuenta con una columna que puede funcionar como natural id, solo faltaría agregar la restricción de único y not null para asegurar la integridad de los datos.