CodeGym /Courses /Spring Data JPA /Practical mini-shop walkthrough

Practical mini-shop walkthrough

Spring Data JPA
Level 1, Lesson 4
Available

1. Hard project boundaries 🤯

There are two extremes. The first is to take a project that is too toy-like, where you have two entities and one table. Then the course turns into something purely demonstrational and does not get close to real backend life. The second is to try to build a “full e-commerce” system, add users, payments, delivery, notifications, security, integrations, and accidentally turn a data course into an endless encyclopedia.

Mini-shop is good because it is lively enough, but it still does not spread in all directions. We have a product catalog, stock tracking, and orders. That is already enough to talk honestly about reads, writes, transactions, and data consistency.

There is one more deliberate boundary: one service and one database. Not because the world always looks like that, but because you first need to learn how to make the data layer predictable inside a single application. Only then does it make sense to spread it across microservices, brokers, and caches. Otherwise, what scales is chaos, not the system.

2. Three areas that make up one data layer

Inside the project there are three domain areas: catalog, inventory, and ordering. They are not random and not symmetric “for beauty.” Each answers its own set of questions about data.

The catalog answers the question: what are we selling. This is where categories, products, names, SKUs, prices, product states, and detailed descriptions live. This is an area with lots of reads: a product card, a catalog list, filtering, search.

Inventory answers the question: how much can be sold right now. This is where stock and reservations live. This is no longer just a reference list. The state changes more often here and is more sensitive to errors. If the catalog can be thought of as a fairly stable showcase, then inventory is the pulsating part of the system.

Ordering answers the question: what exactly did the customer order and how was it recorded historically. Here, the order number, line items, quantity, price at the time of purchase, total amount, and shipping address matter. This area is especially valuable for learning because it quickly brings transaction thinking to the surface.

In short, it can be kept like this:

Area Main question Why it is useful for the course
catalog What are we selling? Gives an entity model, querying, and read-side scenarios
inventory How much is available? Gives consistency, concurrency, and state changes
ordering What did the customer order? Gives multi-entity operations and transaction logic

This one table already shows that the course is not about “random entities.” It is about different classes of problems inside one data layer.

3. Read scenario: show a product card

Let us take a simple scenario from the customer or catalog perspective: we need to show a product card. On the surface, that sounds harmless. But if we look a little closer, several questions are already hiding inside.

What exactly needs to be shown? Name, price, category, description, possibly the manufacturer, possibly stock availability. And here it immediately becomes clear that even “ordinary reading” is rarely reduced to one plain table. Somewhere a relation to category will be needed. Somewhere, product details. Somewhere, stock. And then the question appears: should all of this be read as one object, as several related entities, or as a separate read model?

So even before the first annotation, it is clear that the data layer is not only about “being able to save.” It is also about being able to read data for a specific use case. A product card is not the same as a catalog row. An admin list is not the same as a product detail view. This is where the read side and the question of how to design querying for different scenarios begin to emerge.

And this is an important feeling for the entire course: a repository does not exist in a vacuum. It exists to support specific read and write scenarios, not just to have a nice name.

4. placeOrder() as the heart of the course 💥

If you need one scenario that pulls almost the entire future course into one point, it is placeOrder(). On the outside, it looks natural: the customer places an order. But on the inside, it is already a serious business operation.

The system has to understand which products are included in the order, verify quantities, match products with stock, record the order itself, create order items, calculate the total amount, and change stock state. This is not a single “save object” call. It is a series of related changes that must form one consistent result 🧵

In simplified form, such an operation can be thought of like this:

class OrderService {

    String placeOrder(String customerEmail, List<OrderLineRequest> lines) {
        // Input data: customer email and a list of items (what and how many they want to buy)

        // 1. Load products and stock
        // 2. Check quantity availability
        // 3. Create CustomerOrder
        // 4. Create OrderItem for each line
        // 5. Update StockItem
        // 6. Save changes as one consistent result
      
        // Usually we return an external order identifier (number/code), not the internal DB id
        return "ORD-2025-0001";
    }
}

And this is where it becomes clear why the course is designed this way. From one placeOrder(), almost all major topics naturally grow:

  • what the entity model and relationships look like;
  • where the repository boundary is;
  • where the transaction boundary begins;
  • how ORM forms SQL;
  • how to avoid losing stock consistency;
  • what to do with concurrent changes;
  • how to test such a scenario.

That is the key idea of day one: one ordinary use case already pulls almost the whole course behind it, and not artificially, but by engineering logic 🤯

5. Query scenario

Now let us add another type of task. Not a customer read scenario, but an internal admin one. For example, we need to find products by part of the name, category, and status. Or build a report for products with low stock. These tasks usually do not reduce to “load an entity by id”.

This is where questions become natural: when is a derived query enough, when is JPQL more convenient, when do you need a projection, and when is a targeted native query justified? And all of this is already built into the domain, not imposed artificially. A low-stock report does not come out of nowhere. It grows out of the fact that we have inventory and the business really needs to see products that are about to run out.

This is a very strong signal about the course goals and value. We will not study querying as a collection of random tricks. We will connect the shape of a query with the shape of a use case. And then repository methods stop being “magic names” and become concrete answers to concrete system needs.

6. Where the model boundaries are

Now we can gently outline the future model without turning the lecture into a class inventory. In the catalog, Category and Product will live. These are the basic facts about what is being sold. Product details such as the description or manufacturer are logically kept separately as ProductDetails, because not every catalog read should drag all detailed fields along.

In inventory, StockItem will live, because stock is a separate kind of state. It is not the same as a product. A product has a name and a price. Stock has available and, perhaps later, reserved quantity. Mixing all of this into one “everything object” is usually inconvenient for both the model and the reads.

In ordering, CustomerOrder and OrderItem will live. These are also fundamentally different things. An order is the root fact of the business operation. Order items are its contents. The price in OrderItem is fixed as part of history, because the order must survive future price changes in the catalog. The shipping address in our project is part of the order, not a separate independent subsystem.

At the project structure level, it is useful to think in terms of feature boundaries right away, not “one big entity folder for the whole application”:

shop-data-jpa/          # Project/module root with the data layer
├── catalog/            # Everything about "what we sell": categories, products, catalog reads
├── inventory/          # Everything about "how much is available": stock, reservations, stock contention
└── ordering/           # Everything about "how we place orders": orders, items, transactional scenarios

This is a small but important architectural move. It helps preserve the project’s semantic areas from the very beginning.

7. Day table: mini-shop persistence map

Below is the main reusable artifact of day one. Keep it. This is not just a table of “what will be in the course,” but a working map: which persistence question grows from which scenario. It is handy later when checking whether JPA is turning into magic.

Scenario Which data must become durable state What the persistence question is inside it Where the course will cover it
Show a product card Product, Category, ProductDetails, sometimes StockItem How to read for a specific use case, avoid pulling unnecessary data, understand relationships and JOIN Entity mapping, querying, fetch planning
Create or update a product Product, relation to Category, field constraints How to design an entity and repository without a pile of methods Entity model, basic repositories, constraints
Place an order (placeOrder()) CustomerOrder, OrderItem, update of StockItem Where the transaction boundary is, how to maintain consistency and history Transactions, persistence context, locking
Find products by filters Catalog data and statuses How to express read-side scenarios of varying complexity Derived queries, JPQL, projections, specifications
Build a low-stock report Current stock and related products When a projection is needed and when an SQL-oriented report is justified Querying, projections, targeted native queries
Evolve the schema Tables, keys, constraints, indexes Who is the source of truth for the schema and how to avoid living on manual fixes Flyway, DB constraints, schema discipline
Check all this with tests Test data, repositories, service scenarios How to test the data layer separately and meaningfully @DataJpaTest, service integration tests

This is where the feeling appears that the course is designed as one engineering story, not as a set of “today one annotation, tomorrow another annotation” 🌟 On one project, mapping, querying, transactions, consistency, migrations, and testing appear naturally. Not because the author wanted to cover everything, but because the domain itself demands it.

8. Self-limitation

It is also important what is deliberately absent from the project. We are not bringing in security, payments, external integrations, messaging, caching, microservices, or Kubernetes. Not because they are “unimportant,” but because right now they would blur the main focus: the data layer inside one Spring Boot application with one PostgreSQL database.

And just as importantly, the course will not pretend to be a full deep dive into Hibernate. We will talk about Hibernate exactly as much as needed to understand ORM behavior in ordinary application development. Deep runtime mechanics, fine-grained performance, and internals diagnostics are the next level and a separate course 🔬

Such an honest boundary is not a limitation for the sake of limitation. It is a way to make the course as useful as possible. When the boundaries are transparent, the student understands why each topic is here and why there are not ten extra branches next to it. Trust in the course is very often built exactly on this: the author knows when to stop, not only when to add one more trendy technology 👍

9. Next step

After such a walkthrough, it is already clear that the next step of the course should be focused on SQL. If the product card, stock, and order all still sit on tables, keys, and JOIN, then without a relational foundation there will only be the illusion of understanding. So the next natural question is not “which annotation should be placed first,” but “how are tables, relationships, constraints, and read/write operations for this model actually thought about.”

And after that, the route also looks logical. First the SQL foundation and infrastructure. Then the entity model and basic repositories. Then querying. Then transaction thinking. Then lazy loading, fetch planning, consistency, Flyway, and tests. In other words, the course does not throw topics at you. It answers, step by step, the questions that are already visible in mini-shop.

There is another useful positioning line here. This is a place where we can boast a little 😅 When this course ends, you will not just have “knowledge of a few annotations,” but a properly assembled data layer. So the next natural steps will be Spring Test and Hibernate deep-dive. The first will strengthen system testability, the second will deepen understanding of ORM runtime behavior. But first we need the foundation of the current course, and the first level was meant to show exactly why.

10. Typical mistakes 🚫

Mistake #1: treating the project as a list of entities rather than a set of scenarios.
As soon as you stop seeing the use case, the model quickly becomes random. And with it, the repositories become random too.

Mistake #2: trying to turn mini-shop into a “full e-commerce” system.
That immediately kills the focus. The course is stronger precisely because the project is constrained and forces you to deeply understand the data layer, not skim over everything superficially.

Mistake #3: thinking that placeOrder() is “well, later, when we get to transactions.”
On the contrary, even now it is useful to see that one such scenario is the very reason half of the course topics exist.

Mistake #4: considering a project walkthrough something secondary compared to theory.
In fact, this is the real-life test of the theory. If an idea does not survive a normal domain scenario, then it was explained too abstractly.

1
Task
Spring Data JPA, level 1, lesson 4
Locked
JSON map of domain areas for mini-shop
JSON map of domain areas for mini-shop
1
Task
Spring Data JPA, level 1, lesson 4
Locked
Packages and Classes of the Domain Map
Packages and Classes of the Domain Map
1
Survey/quiz
Data layer, level 1, lesson 4
Unavailable
Data layer
Persistence in the application
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION