Skip to content

Chapter 1: Product Catalogue Microservice

Reyas Khan M edited this page Jun 5, 2025 · 1 revision

Chapter 1: Shopfront Microservice

Welcome to the first chapter of our tutorial! We're going to build a cool Java application using Docker and Kubernetes. But before we dive into all the technical stuff like containers and orchestration, let's start with the basics: understanding the different parts of our application.

Imagine you're building an online shop. What's the first thing a customer sees or interacts with? It's the shop window and the person at the counter, right? In the world of software, this is often called the "frontend" or "user interface". In our project, we're calling this the Shopfront Microservice.

What is the Shopfront Microservice?

Think of the Shopfront Microservice as the face of our online store. It's the part that users (our customers) interact with directly. It's responsible for:

  1. Displaying Information: Like showing a list of all the products available, their names, descriptions, prices, and how many are in stock.
  2. Handling User Requests: When a user clicks on a product or visits the main page, the Shopfront handles that request.
  3. Gathering Data: The Shopfront doesn't know everything itself. It needs to ask other parts of the system for information, like getting product details or checking stock levels.
  4. Putting it Together: It takes the information from different sources and combines it into a nice view for the user (like a web page) or provides it through its own data interface (an API).

So, its main job is to be the main entry point for our users and to make sure they see the right information, even if that information comes from different places.

Why do we need a separate Shopfront service?

In traditional applications, all the parts (showing the website, managing products, tracking stock) might be bundled together. This is like having one big, complex machine. It works, but if one small part breaks, the whole machine might stop. Also, it's hard for different teams to work on different parts at the same time without getting in each other's way.

Using a microservice approach means breaking our application into smaller, independent pieces. The Shopfront is one of these pieces. By making the Shopfront a separate service, we achieve several things:

  • Simplicity: The Shopfront only needs to worry about handling user requests and displaying data. It doesn't need to manage product details or stock levels itself.
  • Maintainability: If we want to change how the shop window looks, we only work on the Shopfront service. We don't touch the parts that manage stock or product data.
  • Scalability: If lots of users visit the shop at once, we can easily make more copies of just the Shopfront service to handle the load, without needing to scale the other parts.

How the Shopfront Shows Products

Let's look at the core task of the Shopfront: showing a list of products to the customer.

To do this, the Shopfront needs two main pieces of information for each product:

  1. Product Details: Like the product's name, description, and price.
  2. Stock Information: How many of that product are currently available.

These two types of information are managed by different services in our application:

The Shopfront's job is to ask both of these services for their information, combine it for each product, and then display the final result.

Here's a simple diagram showing this interaction:

sequenceDiagram
    participant Browser as User's Browser
    participant Shopfront as Shopfront Microservice
    participant ProductCatalogue as Product Catalogue Microservice
    participant StockManager as Stock Manager Microservice

    Browser->>Shopfront: Request Home Page (Show Products)
    Shopfront->>ProductCatalogue: Ask for Product Details
    ProductCatalogue-->>Shopfront: Send Product Details (Names, Prices etc.)
    Shopfront->>StockManager: Ask for Stock Levels
    StockManager-->>Shopfront: Send Stock Info (Amounts Available)
    Shopfront->>Shopfront: Combine Product & Stock Info
    Shopfront-->>Browser: Display Products on Web Page
Loading

The Shopfront acts as the aggregator or gateway here. It doesn't hold the product or stock data itself; it fetches it from the services that specialize in managing that data. We'll look at how services talk to each other using APIs in Chapter 4: Microservice Communication (REST APIs).

Looking at the Shopfront Code

Let's peek at some files within the shopfront directory to see how this is put together in our Java project using the Spring Boot framework.

First, where the application runs and gets basic settings:

// shopfront/src/main/java/uk/co/danielbryant/djshopping/shopfront/ShopfrontApplication.java
package uk.co.danielbryant.djshopping.shopfront;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableHystrix
public class ShopfrontApplication {

    public static void main(String[] args) {
        SpringApplication.run(ShopfrontApplication.class, args);
    }

    // This bean helps us make requests to other services
    @Bean(name = "stdRestTemplate")
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

This is the main class that starts our Shopfront application using Spring Boot. It also sets up a RestTemplate, which is a tool we'll use later to talk to the other microservices.

Next, the configuration that tells the Shopfront where to find the other services:

# shopfront/src/main/resources/application.properties
server.port = 8010
productCatalogueUri = http://productcatalogue:8020
stockManagerUri = http://stockmanager:8030

This file sets the port the Shopfront will run on (8010) and importantly, it lists the addresses (URIs) of the Product Catalogue and Stock Manager services. Notice the names productcatalogue and stockmanager - these are how the Shopfront expects to find the other services, which will become clear when we use Docker and Kubernetes later.

Now, let's see which code handles the request for the main page:

// shopfront/src/main/java/uk/co/danielbryant/djshopping/shopfront/controllers/HomeController.java
package uk.co.danielbryant.djshopping.shopfront.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import uk.co.danielbryant.djshopping.shopfront.services.ProductService;

@Controller
public class HomeController {

    @Autowired
    private ProductService productService;

    @RequestMapping("/")
    public String index(Model model) {
        // Ask the ProductService to get the list of products
        model.addAttribute("products", productService.getProducts());
        // Tell Spring to render the 'index.html' template
        return "index";
    }
}

This HomeController listens for requests to the root path (/), which is typically the home page. When a request comes in, it calls something called productService.getProducts(). This is where the Shopfront starts the process of gathering product and stock data.

Let's look at that ProductService:

// shopfront/src/main/java/uk/co/danielbryant/djshopping/shopfront/services/ProductService.java
package uk.co.danielbryant.djshopping.shopfront.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import uk.co.danielbryant.djshopping.shopfront.model.Product;
// Imports for StockRepo, ProductRepo, DTOs, etc.
// ... (other imports) ...
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ProductService {

    @Autowired
    private StockRepo stockRepo; // Will talk to Stock Manager

    @Autowired
    private ProductRepo productRepo; // Will talk to Product Catalogue

    public List<Product> getProducts() {
        // 1. Get product details from Product Catalogue
        Map<String, ProductDTO> productDTOs = productRepo.getProductDTOs();
        // 2. Get stock info from Stock Manager
        Map<String, StockDTO> stockDTOMap = stockRepo.getStockDTOs();

        // 3. Combine the data
        return productDTOs.values().stream()
                .map(productDTO -> {
                    StockDTO stockDTO = stockDTOMap.get(productDTO.getId());
                    // Create a final Product object with combined info
                    return new Product(productDTO.getId(), stockDTO.getSku(),
                        productDTO.getName(), productDTO.getDescription(),
                        productDTO.getPrice(), stockDTO.getAmountAvailable());
                })
                .collect(Collectors.toList());
    }

    // ... (other methods) ...
}

The ProductService is the core logic here. It uses ProductRepo and StockRepo (which are components that know how to talk to the other microservices) to get the raw product details (ProductDTOs) and stock levels (StockDTOs). It then goes through the list of products, finds the matching stock information for each, and creates a new list of Product objects that contain both pieces of information. This combined list is then passed back to the HomeController.

Finally, let's see where that data is displayed:

<!-- shopfront/src/main/resources/templates/index.html -->
<!DOCTYPE html>
<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <!-- ... (meta, title, links to CSS/JS) ... -->
    <title>Welcome to the Docker Java Shopfront!</title>
    <!-- ... (more head content) ... -->
</head>
<body>
<!-- ... (navigation bar) ... -->

<div class="container-fluid">
    <div class="row">
        <!-- ... (sidebar) ... -->
        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
            <h1 class="page-header">Welcome to the Docker Java Shopfront!</h1>
            <h2 class="sub-header">Please select a product!</h2>
            <div class="table-responsive">

                <table class="table table-striped" id="product-table">
                    <thead>
                    <tr>
                        <th>Product Num</th>
                        <th>SKU</th>
                        <th>Name</th>
                        <th>Description</th>
                        <th>Price £</th>
                        <th>Qty Available</th>
                    </tr>
                    </thead>
                    <tbody>
                    <!-- This part loops through the 'products' data provided by the controller -->
                    <tr th:each="product : ${products}">
                        <td th:text="${product.id}">1</td>
                        <td th:text="${product.sku}">12345678</td>
                        <td th:text="${product.name}">Widget</td>
                        <td th:text="${product.description}">Widget</td>
                        <td th:text="${#numbers.formatDecimal(product.price, 0, 'COMMA', 2, 'POINT')}">1.19</td>
                        <td th:text="${product.amountAvailable}">2</td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>
<!-- ... (scripts) ... -->
</body>
</html>

This is the HTML template (index.html) that the HomeController uses. It's a standard web page structure using Bootstrap for styling. The important part is the section inside <tbody>. The th:each="product : ${products}" part tells the template engine (Thymeleaf) to loop through the list of products that the HomeController added to the model. For each product, it creates a table row (<tr>) and fills in the columns (<td>) with the product's details using th:text="${product.propertyName}". This is how the data fetched and combined by the ProductService is finally displayed to the user.

Packaging the Shopfront

Once our Shopfront code is ready, we need to package it so it can run. We use Maven (pom.xml) to build the Java code and manage dependencies (like needing Spring Boot).

<!-- shopfront/pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>uk.co.danielbryant.djshopping</groupId>
    <artifactId>shopfront</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <!-- ... (name, description, parent, properties) ... -->

    <dependencies>
        <!-- Dependency for building web pages -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- ... (other dependencies like actuator, hystrix, eureka, test) ... -->
    </dependencies>

    <build>
        <plugins>
            <!-- Plugin to build an executable JAR -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!-- ... (other plugins) ... -->
        </plugins>
    </build>
</project>

This pom.xml file tells Maven how to build the project. The key part is the spring-boot-maven-plugin in the <build> section. This plugin is configured to create a single, executable .jar file that contains all our code and its dependencies, making it easy to run.

Finally, to make it ready for Docker, we use a Dockerfile:

# shopfront/Dockerfile
FROM openjdk:8-jre
ADD target/shopfront-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8010
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

This Dockerfile is a recipe for creating a Docker image for our Shopfront.

  • FROM openjdk:8-jre: Start from an official Java 8 runtime image. This gives us the basic environment to run our Java app.
  • ADD target/shopfront-0.0.1-SNAPSHOT.jar app.jar: Copy the executable JAR file we built with Maven into the Docker image, renaming it to app.jar.
  • EXPOSE 8010: Inform Docker that this container will listen on port 8010 (as configured in application.properties). This is documentation; it doesn't actually publish the port outside the container.
  • ENTRYPOINT [...]: This command tells Docker what to run when the container starts. In this case, it runs the Java command to execute the app.jar file.

This Dockerfile packages our Shopfront application into a self-contained unit, ready to be run consistently anywhere that has Docker installed. We'll explore Docker containers in more detail in Chapter 6: Docker Container Packaging.

Conclusion

In this chapter, we introduced the Shopfront Microservice as the user-facing part of our application. We learned that it acts like the "shop window" and "cashier", handling user requests, gathering information from other services (Product Catalogue Microservice and Stock Manager Microservice), combining that information, and displaying it to the user. We also saw how its configuration tells it where to find these other services and how its code fetches and combines the data before rendering a web page. Finally, we saw how the pom.xml and Dockerfile prepare the Shopfront for running inside a container.

Now that we understand the role of the Shopfront and what information it needs, let's move on to the next piece of the puzzle: the Product Catalogue Microservice, which holds the details about our products.

Next Chapter: Product Catalogue Microservice


Doc by Reyas Khan. References: [1], [2], [3], [4], [5], [6], [7], [8]

Clone this wiki locally