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. The microservices will handle employee and department data, and Docker will help us deploy these services in isolated containers.

What You’ll Learn:

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

This guide is designed for beginners and includes detailed explanations for each step.

Spring Boot Microservices with Docker Example

Introduction to Microservices and Docker

In a microservices architecture, an application is split into multiple smaller services, each responsible for a specific feature. Each service can be developed, deployed, and scaled independently. Docker simplifies the deployment process by allowing developers to package these microservices into containers.

Prerequisites

Before starting, ensure you have the following tools installed:

  • JDK 17 or later
  • Maven or Gradle
  • Docker
  • IDE (like IntelliJ IDEA or Eclipse)

Step 1: Create Two Spring Boot Projects

We will create two Spring Boot microservices:

  1. employee-service: Manages employee details.
  2. department-service: Manages department details and communicates with employee-service to fetch employee information.

Why Two Services?

This example demonstrates how microservices communicate with each other using REST APIs, with each service containerized and running independently.

Step 2: Set Up employee-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

Configure the application properties for employee-service.

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

Explanation:

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

2.3 Create the Employee Model

Create an Employee class to represent employee data.

package com.example.employeeservice;

public class Employee {
    private String id;
    private String name;
    private String department;

    // Constructor, getters, and setters
    public Employee(String id, String name, String department) {
        this.id = id;
        this.name = name;
        this.department = department;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getDepartment() {
        return department;
    }
}

Explanation:

  • This class models an employee with attributes like id, name, and department.

2.4 Create a Controller

Create a REST controller to expose employee-related endpoints.

package com.example.employeeservice;

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

@RestController
public class EmployeeController {

    @GetMapping("/employees/{id}")
    public Employee getEmployee(@PathVariable String id) {
        return new Employee(id, "John Doe", "Engineering");
    }
}

Explanation:

  • @RestController: Defines the class as a REST controller.
  • @GetMapping("/employees/{id}"): Maps GET requests to return employee 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/employee-service-0.0.1-SNAPSHOT.jar employee-service.jar

# Expose port 8081
EXPOSE 8081

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

Step 3: Set Up department-service

3.1 Create the Project

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

  • Spring Web
  • Spring Boot Actuator
  • OpenFeign (for inter-service communication)

3.2 Configure application.properties

Set up the application properties for department-service.

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

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

Explanation:

  • employee.service.url: Specifies the URL for the employee-service to enable communication between services.

3.3 Enable Feign Client

Add the @EnableFeignClients annotation in the main application class:

package com.example.departmentservice;

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

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

3.4 Create a Feign Client Interface

Define a Feign client to communicate with the employee-service:

package com.example.departmentservice;

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

@FeignClient(name = "employee-service", url = "${employee.service.url}")
public interface EmployeeServiceClient {
    @GetMapping("/employees/{id}")
    Employee getEmployeeById(@PathVariable String id);
}

3.5 Create the Employee Model

Create an Employee class in department-service that matches the structure in employee-service:

package com.example.departmentservice;

public class Employee {
    private String id;
    private String name;
    private String department;

    // Constructor, getters, and setters
    public Employee() {}

    public Employee(String id, String name, String department) {
        this.id = id;
        this.name = name;
        this.department = department;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getDepartment() {
        return department;
    }
}

3.6 Create a Controller

Create a controller in department-service to handle department-related requests and fetch employee details.

package com.example.departmentservice;

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

@RestController
public class DepartmentController {

    private final EmployeeServiceClient employeeServiceClient;

    public DepartmentController(EmployeeServiceClient employeeServiceClient) {
        this.employeeServiceClient = employeeServiceClient;
    }

    @GetMapping("/departments/{employeeId}")
    public String getEmployeeDepartment(@PathVariable String employeeId) {
        Employee employee = employeeServiceClient.getEmployeeById(employeeId);
        return "Employee " + employee.getName() + " works in the " + employee.getDepartment() + " department.";
    }
}

3.7 Create a Dockerfile for department-service

Create a Dockerfile in the root directory of the department-service 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/department-service-0.0.1-SNAPSHOT.jar department-service.jar

# Expose port 8082
EXPOSE 8082

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

Explanation:

  • FROM openjdk:17-jdk-alpine: Uses an official JDK 17 image.
  • WORKDIR /app: Sets the working directory within the container.
  • COPY target/department-service-0.0.1-SNAPSHOT.jar department-service.jar: Copies the generated JAR file into the container.
  • EXPOSE 8082: Exposes port 8082 for the department-service.
  • ENTRYPOINT: Specifies the command to start the application when the container starts.

Step 4: Build Docker Images

Build Docker images for both services.

For employee-service:

mvn clean package
docker build -t employee-service .

For department-service:

mvn clean package
docker build -t department-service .

Step 5: Create a Docker Network

Create a Docker network so the containers can communicate with each other:

docker network create microservices-net

Step 6: Run the Microservices

Run the Docker containers:

docker run -d --net microservices-net --name employee-service -p 8081:8081 employee-service
docker run -d --net microservices-net --name department-service -p 8082:8082 department-service

Step 7: Test the Microservices

Test the services using a browser or Postman.

You should see a response from department-service that includes employee details retrieved from employee-service.

Conclusion

You have successfully created two Spring Boot microservices (employee-service and department-service), containerized them with Docker, and enabled communication between them using Feign clients. This architecture can be scaled to include more services or more advanced features like service discovery, load balancing, or database integration.

Next Steps:

  • Add databases to both services (e.g., MySQL, PostgreSQL).
  • Implement service discovery using Spring Cloud Netflix Eureka.

Comments