Saltar al contenido
arquitectura / · 4 min

Aprender Spring Boot contra una base de Laravel con 249 mil registros

En vez del CRUD de mascotas, reescribi una rebanada de una API que ya existe. Bcrypt compartido, ddl-auto en none, Specification y la idempotencia que solo se te ocurre con una red que se corta.

Administrador · Desarrollador Laravel
Aprender Spring Boot contra una base de Laravel con 249 mil registros

Quería aprender Spring Boot. Podía hacer el CRUD de mascotas del tutorial, o podía reescribir una rebanada de una API que ya existe, contra una base con 249,691 registros reales, roles reales y reglas de negocio que no me inventé yo.

Elegí lo segundo, y aprendí cosas que un CRUD de juguete no enseña.

Cuatro endpoints, no cuarenta

El alcance fue deliberadamente pequeño: login, listar, crear y borrado suave. Lo interesante no era la cantidad, era que cada uno tocaba una pieza distinta del framework.

RutaQué ejercita POST /auth/loginSpring Security, bcrypt, emisión de token GET /promovidosJPA, Specification, paginación, alcance POST /promovidosBean Validation, alta idempotente por UUID DELETE /promovidos/{id}Borrado suave, permisos por fila ## Lo primero: no tocar el esquema

La base es de la aplicación Laravel. Hibernate, si lo dejas, te "ayuda" creando y alterando tablas para que encajen con tus entidades. Eso ahí sería un desastre.

spring.jpa.hibernate.ddl-auto=none

Esa línea es la primera que escribí. Las entidades se adaptan a la base, no al revés:

@Entity
@Table(name = "tbl_integrantes_redes", schema = "app")
public class Integrante { ... }

Nombres de tabla en español, en un esquema que no es public, con columnas que nadie diseñó pensando en JPA. Se puede. Solo hay que decirlo explícito en cada anotación en vez de confiar en las convenciones.

Los hashes de Laravel los verifica Spring sin traducción

Esta me sorprendió. Laravel guarda contraseñas con bcrypt; Spring Security trae BCryptPasswordEncoder. Son el mismo algoritmo y el mismo formato de cadena ($2y$...), así que un usuario dado de alta desde Laravel entra desde el servicio Java sin migrar nada ni pedirle que cambie su contraseña.

Es un buen recordatorio de que bcrypt es un estándar, no una cosa de Laravel. Lo mismo aplica al revés.

Specification: el equivalente de los scopes

Viniendo de Eloquent, uno espera poder encadenar condiciones opcionales. El equivalente en JPA es Specification, y es más ceremonioso pero hace lo mismo:

public static Specification<Integrante> conNombre(String q) {
    return (raiz, consulta, cb) -> q == null || q.isBlank()
        ? cb.conjunction()
        : cb.like(cb.lower(raiz.get("nombre")), "%" + q.toLowerCase() + "%");
}

Ese cb.conjunction() —un "verdadero" que no filtra nada— es el truco para que un filtro vacío no rompa la cadena. Es literalmente lo que hace when() en un query builder de Laravel.

Idempotencia: el punto que solo aparece con datos de verdad

La API la consume una app móvil que trabaja sin señal y sincroniza después. Eso significa que la misma alta puede llegar dos veces: el teléfono mandó, se cortó la red antes de recibir la respuesta, y reintentó.

Si el POST inserta a ciegas, terminas con el registro duplicado y la culpa parece del cliente. La solución es que el cliente genere un UUID y el servidor lo trate como llave de negocio:

repo.findByUuid(dto.uuid())
    .map(existente -> actualizar(existente, dto))
    .orElseGet(() -> crear(dto));

Reintentar deja de ser peligroso. Este detalle no se le ocurre a nadie haciendo el CRUD de mascotas, porque ahí nunca hay una red que se corte.

Permisos por fila, no solo por endpoint

Que alguien pueda llamar a DELETE /promovidos/{id} no significa que pueda borrar ese promovido. Un promotor administra su gente, no la de todos.

Spring Security resuelve bien la primera parte —quién entra a qué ruta— pero la segunda es lógica de dominio y va en el servicio. Separar esas dos capas mentalmente me costó un rato: venía de un mundo donde el can:permiso.gestionar del middleware y el "¿pero es suyo?" del controlador se sienten como lo mismo.

¿Valió la pena?

Sí, y no por el Java. Reescribir algo que ya funciona en otra tecnología te obliga a entender por qué el original hace lo que hace. Al portar la idempotencia y los permisos por fila me di cuenta de decisiones del Laravel original que había tomado sin pensarlas del todo.

Y queda una demostración concreta de convivencia poliglota: dos servicios en lenguajes distintos, sobre la misma base, sin que ninguno estorbe al otro.

#postgresql #api #arquitectura
Comentarios 0

Nadie ha comentado todavía. Estrena la sección.

Deja tu comentario
Tu correo no se publica. Reviso los comentarios antes de publicarlos.

Seguir leyendo