CodeGym /Cursos /Spring Data JPA /Walkthrough práctico de mini-shop

Walkthrough práctico de mini-shop

Spring Data JPA
Nivel 1, Lección 4
Disponible

1. Los límites firmes del proyecto 🤯

Hay dos extremos. El primero es tomar un proyecto demasiado de juguete, con dos entidades y una tabla. El curso queda puramente demostrativo y no llega a la vida real del backend. El segundo es intentar construir un «e-commerce completo», añadir usuarios, pagos, entrega, notificaciones, seguridad, integraciones y convertir accidentalmente el data-course en una enciclopedia interminable.

Mini-shop es bueno porque tiene suficiente vida pero no se desborda en todas las direcciones. Tenemos catálogo de productos, gestión de stock y pedidos. Eso ya es suficiente para hablar honestamente de lecturas y escrituras, transacciones y coherencia de datos.

Hay también otro límite consciente: un servicio y una base de datos. No porque el mundo sea siempre así, sino porque la capa de datos hay que aprender primero a hacerla predecible dentro de una única aplicación. Y solo después tiene sentido dispersarla entre microservicios, brokers y caché. De lo contrario lo que escala es el caos, no el sistema.

2. Tres zonas que forman una sola capa de datos

Dentro del proyecto hay tres zonas temáticas: catalog, inventory y ordering. No son casuales ni simétricas «por belleza». Cada una responde a su propio conjunto de preguntas sobre los datos.

El catálogo responde a la pregunta: qué vendemos. Aquí viven las categorías, los productos, los nombres, los artículos, los precios, los estados de los productos y las descripciones detalladas. Es una zona donde habrá muchas lecturas: ficha del producto, lista del catálogo, filtrado, búsqueda.

Inventory responde a la pregunta: cuánto de esto se puede vender ahora mismo. Aquí viven el stock y las reservas. Esto ya no es solo un directorio. Aquí el estado cambia más frecuentemente y es más sensible a errores. Si el catálogo puede imaginarse como un escaparate bastante estable, inventory es la parte pulsante del sistema.

Ordering responde a la pregunta: qué pidió exactamente el cliente y cómo está registrado históricamente. Aquí son importantes el número de pedido, las líneas, las cantidades, el precio en el momento de la compra, el importe total, la dirección de entrega. Esta zona es especialmente valiosa para el aprendizaje, porque saca rápidamente a la superficie el pensamiento transaccional.

Brevemente puede resumirse así:

Zona Pregunta principal Por qué es útil para el curso
catalog ¿Qué vendemos? Proporciona el modelo de entidades, consultas y escenarios de lectura
inventory ¿Cuánto hay disponible? Proporciona coherencia, concurrencia y cambios de estado
ordering ¿Qué pidió el cliente? Proporciona operaciones con múltiples entidades y lógica transaccional

Ya solo con esta tabla se ve que el curso no trata de «entidades aleatorias». Trata de distintas clases de tareas dentro de una sola capa de datos.

3. Escenario de lectura: mostrar la ficha de un producto

Tomemos un escenario sencillo desde el punto de vista del cliente o del catálogo: hay que mostrar la ficha de un producto. En palabras parece inofensivo. Pero si nos fijamos un poco, ya hay varias preguntas escondidas dentro.

¿Qué hay que mostrar exactamente? El nombre, el precio, la categoría, la descripción, posiblemente el fabricante, posiblemente la disponibilidad en stock. Y aquí se ve enseguida que incluso «una lectura normal» raramente se reduce a una tabla sola. En algún lugar hará falta la relación con la categoría. En otro — los detalles del producto. En otro — el stock. Y luego surge la pregunta: ¿todo esto debe leerse como un objeto único, como varias entidades relacionadas o como un modelo de lectura separado?

Es decir, ya antes de la primera anotación se ve que la capa de datos no es solo «saber guardar». También es saber leer los datos para un caso de uso concreto. La ficha de un producto no es lo mismo que una fila del catálogo. La lista de administración no es lo mismo que el detalle del producto. Aquí empieza ya a nacer el tema del read-side y de cómo diseñar las consultas para distintos escenarios.

Y esta es una sensación importante para todo el curso: el repositorio no existe en el vacío. Es necesario para sostener escenarios concretos de lectura y escritura, no para simplemente tener un nombre bonito.

4. placeOrder() como núcleo del curso 💥

Si se necesita un único escenario que reúna casi todo el curso futuro en un punto, ese es placeOrder(). Por fuera parece natural: el cliente gestiona un pedido. Pero por dentro es ya una operación de negocio seria.

El sistema debe entender qué productos forman parte del pedido, verificar las cantidades, cotejar los productos con el stock, registrar el propio pedido, crear las líneas del pedido, calcular el importe total y cambiar el estado del stock. No es una llamada a «guardar objeto». Es una serie de cambios relacionados que deben formar un resultado coherente único 🧵

Una operación así puede pensarse simplificadamente de este modo:

class OrderService {

    String placeOrder(String customerEmail, List<OrderLineRequest> lines) {
        // Datos de entrada: email del cliente y lista de líneas (qué y en qué cantidad quieren comprar)

        // 1. Cargar productos y stock
        // 2. Verificar la disponibilidad de cantidades
        // 3. Crear CustomerOrder
        // 4. Crear OrderItem para cada línea
        // 5. Cambiar StockItem
        // 6. Guardar los cambios como un resultado coherente único

        // Como respuesta normalmente devolvemos el identificador externo del pedido (número/código), no el id interno de la BD
        return "ORD-2025-0001";
    }
}

Y aquí se ve por qué el curso está diseñado exactamente así. De un solo placeOrder() surgen naturalmente casi todos los grandes temas:

  • cómo es el modelo de entidades y las relaciones;
  • dónde pasa el límite del repositorio;
  • dónde empieza el límite de la transacción;
  • cómo genera SQL el ORM;
  • cómo no perder la coherencia del stock;
  • qué hacer con los cambios concurrentes;
  • cómo testear ese escenario.

Esta es la idea clave del primer día: un caso de uso ordinario ya arrastra consigo casi todo el curso, y no de forma artificial, sino por lógica ingenieril 🤯

5. Escenario de selección

Ahora añadamos otro tipo de tarea. No un escenario de lectura del cliente, sino uno interno de administración. Por ejemplo, hay que encontrar productos por parte del nombre, por categoría y por estado. O construir un informe de productos con poco stock. Estas tareas normalmente no se reducen a «cargar una entidad por id».

Aquí se vuelven ya naturales preguntas como: cuándo basta una derived query, cuándo es más cómodo JPQL, cuándo hace falta una proyección y cuándo está justificada una consulta nativa puntual. Y todo esto ya está integrado en el dominio, no impuesto artificialmente. El informe de bajo stock no sale de la nada. Surge del hecho de que tenemos inventory y al negocio le importa realmente ver los productos que pronto se agotarán.

Esta es una señal muy fuerte sobre los objetivos y el valor del curso. No vamos a estudiar las consultas como una colección de trucos aleatorios. Vamos a vincular la forma de la consulta con la forma del caso de uso. Y entonces los métodos del repositorio dejan de ser «nombres mágicos» y se convierten en respuestas concretas a necesidades concretas del sistema.

6. Dónde pasan los límites del modelo

Ahora podemos esbozar suavemente el modelo futuro sin convertir la lección en un inventario de clases. En el catálogo vivirán Category y Product. Son los hechos básicos sobre qué se vende. Los detalles del producto como la descripción o el fabricante tienen sentido mantenerlos aparte como ProductDetails, porque no toda lectura del catálogo tiene que cargar todos los campos detallados.

En inventory vivirá StockItem, porque el stock es un tipo de estado separado. No es lo mismo que el producto. Un producto tiene nombre y precio. Un stock tiene la cantidad disponible y posiblemente la reservada. Mezclar estos datos en un solo «todo-objeto» suele ser inconveniente tanto para el modelo como para las lecturas.

En ordering habrá CustomerOrder y OrderItem. Son también cosas fundamentalmente distintas. El pedido es el hecho raíz de la operación de negocio. Las líneas del pedido son su composición. El precio en OrderItem se fija como parte de la historia, porque el pedido debe sobrevivir a los futuros cambios de precio en el catálogo. La dirección de entrega en nuestro proyecto es parte del pedido, no un subsistema independiente.

A nivel de estructura del proyecto es útil pensar de entrada por límites de funcionalidades, no en «una gran carpeta entity para toda la aplicación»:

shop-data-jpa/          # Raíz del proyecto/módulo con la capa de datos
├── catalog/            # Todo sobre «qué vendemos»: categorías, productos, descripciones, lecturas del catálogo
├── inventory/          # Todo sobre «cuánto hay disponible»: stock, reservas, concurrencia sobre el stock
└── ordering/           # Todo sobre «cómo gestionamos»: pedidos, líneas, escenarios transaccionales

Este es un gesto arquitectónico pequeño pero importante. Ayuda a no perder las zonas semánticas del proyecto desde el principio.

7. La tabla del día: mini-shop persistence map

A continuación figura el principal artefacto transferible del primer día. Guárdalo. No es solo una tabla de «qué habrá en el curso», sino un mapa de trabajo: de qué escenario surge qué pregunta de persistencia. Con él es cómodo comprobar después si JPA se está convirtiendo en magia.

Escenario Qué datos deben convertirse en estado estable Qué pregunta de persistencia hay dentro Dónde lo cierra el curso
Mostrar la ficha de un producto Product, Category, ProductDetails, a veces StockItem Cómo leer para el caso de uso concreto, no traer datos innecesarios, entender las relaciones y los JOIN Entity mapping, querying, fetch planning
Crear o modificar un producto Product, relación con Category, restricciones de campos Cómo diseñar la entidad y el repositorio sin un montón de métodos Entity model, basic repositories, constraints
Gestionar un pedido (placeOrder()) CustomerOrder, OrderItem, cambio de StockItem Dónde está el límite de la transacción, cómo mantener la coherencia y el historial Transactions, persistence context, locking
Buscar productos por filtros Datos del catálogo y estados Cómo expresar escenarios de lectura de distinta complejidad Derived queries, JPQL, projections, specifications
Construir un informe de bajo stock Stock actual y productos relacionados Cuándo hace falta una proyección y cuándo está justificado un informe orientado a SQL Querying, projections, native queries puntuales
Evolucionar el esquema Tablas, claves, restricciones, índices Quién es la fuente de verdad para el esquema y cómo no vivir de ediciones manuales Flyway, DB constraints, schema discipline
Verificarlo todo con tests Datos de test, repositorios, escenarios de servicio Cómo testear la capa de datos de forma separada y con sentido @DataJpaTest, service integration tests

Aquí es donde surge la sensación de que el curso está diseñado como una historia de ingeniería única, y no como «hoy una anotación, mañana otra» 🌟 En un solo proyecto aparecen naturalmente el mapping, las consultas, las transacciones, la coherencia, las migraciones y los tests. No porque el autor haya querido abarcarlo todo, sino porque el propio dominio lo requiere.

8. La autolimitación

También es importante lo que conscientemente no hay en el proyecto. No traemos aquí seguridad, pagos, integraciones externas, mensajería, caché, microservicios ni Kubernetes. No porque sean «sin importancia», sino porque ahora mismo difuminarían el foco principal: la capa de datos dentro de una única aplicación Spring Boot con una única base de datos PostgreSQL.

Y el curso tampoco va a fingir un deep-dive completo en Hibernate. Hablaremos de Hibernate exactamente en la medida en que sea necesario para entender el comportamiento del ORM en el desarrollo aplicado ordinario. La mecánica de runtime profunda, el rendimiento fino y el diagnóstico de internals — ese ya es el siguiente nivel y un curso aparte 🔬

Un límite honesto así no es una limitación por limitación. Es una forma de hacer el curso lo más útil posible. Cuando los marcos son transparentes, el estudiante entiende para qué está aquí cada tema y por qué no hay diez ramas adicionales al lado. La confianza en el curso muy a menudo se construye precisamente así: el autor sabe dónde parar, no solo dónde añadir una nueva tecnología de moda 👍

9. El siguiente paso

Tras este walkthrough ya está claro que el siguiente paso del curso debe estar orientado precisamente al SQL. Si la ficha del producto, el stock y el pedido de todas formas se apoyan en tablas, claves y JOIN, entonces sin el fundamento relacional lo que sigue solo será una ilusión de comprensión. Por eso la siguiente pregunta natural no es «qué anotación poner primero», sino «cómo se piensan las tablas, las relaciones, las restricciones y las operaciones de lectura/escritura para este modelo».

Y el itinerario también parece lógico. Primero el fundamento SQL y la infraestructura. Luego el modelo de entidades y los repositorios básicos. Luego las consultas. Luego el pensamiento transaccional. Luego el lazy loading, el fetch planning, la coherencia, Flyway y los tests. Es decir, el curso no lanza temas. Responde secuencialmente a las preguntas que ya se ven desde mini-shop.

Y hay también una línea de posicionamiento útil. Aquí podemos presumir un poco 😅 Cuando este curso termine, no tendrás simplemente «el conocimiento de unas pocas anotaciones», sino una capa de datos construida. Por eso los siguientes pasos naturales serán Spring Test e Hibernate deep-dive. El primero reforzará la verificabilidad del sistema, el segundo la comprensión del comportamiento de runtime del ORM. Pero primero hace falta el fundamento del curso actual, y el primer nivel debería haber mostrado por qué.

10. Errores típicos 🚫

Error n.º 1: percibir el proyecto como una lista de entidades y no como un conjunto de escenarios.
En cuanto dejas de ver el caso de uso, el modelo se vuelve rápidamente aleatorio. Y junto con él los repositorios también se vuelven aleatorios.

Error n.º 2: intentar convertir mini-shop en un «e-commerce completo».
Esto destruye enseguida el foco. El curso es más fuerte precisamente porque el proyecto está limitado y obliga a comprender a fondo la capa de datos, y no a recorrer todo superficialmente.

Error n.º 3: pensar que placeOrder() es «para luego, cuando lleguemos a las transacciones».
Al contrario, ya ahora es útil ver que exactamente ese escenario es la razón de existir de la mitad de los temas del curso.

Error n.º 4: considerar el walkthrough del proyecto como algo secundario en comparación con la teoría.
En realidad esto es precisamente la comprobación de la viabilidad de la teoría. Si una idea no soporta un escenario de dominio ordinario, significa que se explicó de forma demasiado abstracta.

1
Tarea
Spring Data JPA, nivel 1, lección 4
Bloqueada
Mapa JSON de dominios de mini-shop
Mapa JSON de dominios de mini-shop
1
Tarea
Spring Data JPA, nivel 1, lección 4
Bloqueada
Paquetes y clases del mapa de dominio
Paquetes y clases del mapa de dominio
1
Cuestionario/control
Capa de datos, nivel 1, lección 4
No disponible
Capa de datos
Persistencia en la aplicación
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION