Spring Boot Microservices with Docker Example

In this tutorial, we will create two Spring Boot microservices, containerize them using Docker, and establish communication between them. Microservices allow you to develop and deploy applications as a collection of loosely coupled services. As a containerization platform, Docker simplifies the deployment and scaling of these services.

What You’ll Learn:

  • How to create two microservices using Spring Boot: product-service and order-service.
  • How to containerize Spring Boot applications with Docker.
  • How to enable inter-service communication using Feign Client.

This guide is designed for beginners and includes detailed steps and explanations to ensure you can easily follow along.


Introduction to Microservices and Docker

In modern software development, microservices architecture allows developers to break down large applications into smaller, manageable services. Each service is responsible for a specific functionality and communicates over standard protocols such as HTTP, making it easier to scale and maintain applications. Docker helps simplify this process by encapsulating each microservice into containers, ensuring consistent development, testing, and production environments.

Why Use Docker with Microservices?

Docker is a popular tool in the microservices world because:

  • Simplified deployment: Containerization bundles everything your app needs (libraries, environment) into one package.
  • Isolation: Each microservice runs in its own container, ensuring dependencies don't conflict.
  • Scalability: Containers are lightweight and can be scaled independently.

Prerequisites

Before diving in, ensure that you have the following tools installed:

  • JDK 17 or later
  • Maven or Gradle (to build the project)
  • Docker
  • IDE (like IntelliJ IDEA or Eclipse)

Step 1: Create Two Spring Boot Projects

We will create two Spring Boot microservices:

  1. product-service: Provides product details.
  2. order-service: Manages orders and communicates with product-service to fetch product details.

Why Two Services?

By splitting the functionality into two microservices, we demonstrate inter-service communication and show how Docker containers can work together on a network.


Step 2: Set Up product-service

2.1 Create the Project

Go to Spring Initializr and create a new Spring Boot project with the following dependencies:

  • Spring Web
  • Spring Boot Actuator

2.2 Configure application.properties

Set up your application.properties file to define the application name and port.

server.port=8081
spring.application.name=product-service

Explanation:

  • server.port=8081: Specifies the port for product-service.
  • spring.application.name=product-service: Sets the name of the application.

2.3 Create the Product Model

Define a Product class to represent product data.

package com.example.productservice;

public class Product {
    private String id;
    private String name;
    private double price;

    // Constructor, getters, and setters
    public Product(String id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }
}

Explanation:

  • This class models a product with attributes like id, name, and price.

2.4 Create a Controller

Create a REST controller to expose product-related endpoints.

package com.example.productservice;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProductController {

    @GetMapping("/products/{id}")
    public Product getProduct(@PathVariable String id) {
        return new Product(id, "Sample Product", 99.99);
    }
}

Explanation:

  • @RestController: Defines the class as a REST controller.
  • @GetMapping("/products/{id}"): Maps GET requests to this endpoint to return product details.

2.5 Create a Dockerfile

Create a Dockerfile in the root directory of the project:

# Use an official JDK runtime as a parent image
FROM openjdk:17-jdk-alpine

# Set the working directory in the container
WORKDIR /app

# Copy the JAR file into the container
COPY target/product-service-0.0.1-SNAPSHOT.jar product-service.jar

# Expose port 8081
EXPOSE 8081

# Run the application
ENTRYPOINT ["java", "-jar", "product-service.jar"]

Step 3: Set Up order-service

3.1 Create the Project

Go to Spring Initializr and create another Spring Boot project with these dependencies:

  • Spring Web
  • Spring Boot Actuator
  • OpenFeign (for communication between microservices)

3.2 Configure application.properties

server.port=8082
spring.application.name=order-service

# URL of product-service
product.service.url=http://product-service:8081

Explanation:

  • product.service.url: Defines the URL of the product-service for communication in Docker.

3.3 Enable Feign Client

Add the @EnableFeignClients annotation in the main application class:

package com.example.orderservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

3.4 Create a Feign Client Interface

Create a Feign client to call the product-service:

package com.example.orderservice;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name = "product-service", url = "${product.service.url}")
public interface ProductServiceClient {
    @GetMapping("/products/{id}")
    Product getProductById(@PathVariable String id);
}

3.5 Create a Controller

Create a controller in order-service to manage orders:

package com.example.orderservice;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    private final ProductServiceClient productServiceClient;

    public OrderController(ProductServiceClient productServiceClient) {
        this.productServiceClient = productServiceClient;
    }

    @GetMapping("/orders/{productId}")
    public String createOrder(@PathVariable String productId) {
        Product product = productServiceClient.getProductById(productId);
        return "Order created for product: " + product.getName() + " with price: $" + product.getPrice();
    }
}

Step 4: Build Docker Images

Build the Docker images for both services.

For product-service:

mvn clean package
docker build -t product-service .

For order-service:

mvn clean package
docker build -t order-service .

Step 5: Create a Docker Network

Create a Docker network for the services to communicate:

docker network create microservices-net

Step 6: Run the Microservices

Run the containers:

docker run -d --net microservices-net --name product-service -p 8081:8081 product-service
docker run -d --net microservices-net --name order-service -p 8082:8082 order-service

Step 7: Test the Microservices

To test the setup, use Postman or your browser:


Conclusion

You have successfully built two Spring Boot microservices, containerized them using Docker, and enabled inter-service communication via REST APIs and Feign clients. This setup can be further expanded by adding more services or implementing more advanced communication patterns. Docker allows for easy scalability and consistency across environments.

Next Steps:

  • Add more microservices.
  • Implement database connections and service discovery (e.g., using Spring Cloud).

Comments