Spring Boot Microservices with Docker Compose Example

In this tutorial, you will learn how to create two Spring Boot microservices and deploy them using Docker Compose, a tool designed for defining and running multi-container Docker applications. This beginner-friendly guide covers everything from project setup to containerization and inter-service communication.

What You’ll Learn

  • Creating two microservices: employee-service and department-service.
  • Containerizing Spring Boot applications with Docker.
  • Using Docker Compose to manage multi-container applications.

Introduction to Microservices and Docker Compose

Microservices architecture enables the development of small, independent services that communicate with each other via APIs. Each microservice handles a distinct piece of functionality and can be scaled or updated independently. Docker Compose simplifies the orchestration of multi-container applications, making it easier to define and run services simultaneously in isolated environments.

Why Docker Compose?

  • Simplified deployment: Easily define multi-container applications in a single YAML file.
  • Networking: Automatically connects services to a network.
  • Scalability: Scale individual services as needed.

Prerequisites

Make sure you have the following tools installed before getting started:

  • JDK 17 or later
  • Maven or Gradle
  • Docker and Docker Compose
  • IDE (IntelliJ IDEA, Eclipse, etc.)

Step 1: Create the Projects

We'll create two Spring Boot microservices:

  • employee-service: Manages employee details.
  • department-service: Fetches employee data from employee-service.

Step 2: Set Up employee-service

2.1 Create the Project

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

  • Spring Web
  • Spring Boot Actuator

2.2 Configure application.properties

In the src/main/resources/application.properties file, configure the application properties for employee-service:

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

Explanation:

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

2.3 Create an 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;
    }
}

2.4 Create a Controller

Create a REST controller to handle employee-related requests.

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");
    }
}

2.5 Create a Dockerfile

Create a Dockerfile in the root of the employee-service project:

FROM openjdk:17-jdk-alpine
WORKDIR /app
COPY target/employee-service-0.0.1-SNAPSHOT.jar employee-service.jar
EXPOSE 8081
ENTRYPOINT ["java", "-jar", "employee-service.jar"]

Step 3: Set Up department-service

3.1 Create the Project

Create another Spring Boot project using Spring Initializr with the following dependencies:

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

3.2 Configure application.properties

Configure the department-service in the application.properties file:

server.port=8082
spring.application.name=department-service
employee.service.url=http://employee-service:8081

Explanation:

  • employee.service.url: Defines the URL for employee-service for communication.

3.3 Enable Feign Clients

Add the @EnableFeignClients annotation to 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 Feign Client Interface

Create a Feign client to communicate with 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 a Controller

Create a REST controller to fetch employee data through the Feign client:

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.6 Create a Dockerfile

Create a Dockerfile for department-service:

FROM openjdk:17-jdk-alpine
WORKDIR /app
COPY target/department-service-0.0.1-SNAPSHOT.jar department-service.jar
EXPOSE 8082
ENTRYPOINT ["java", "-jar", "department-service.jar"]

Step 4: Build Docker Images

Navigate to the root directories of each service and run the following commands to build Docker images:

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 Docker Compose File

Create a docker-compose.yml file in the root directory to define the multi-container setup:

version: '3.8'

services:
  employee-service:
    image: employee-service
    build:
      context: ./employee-service
    ports:
      - "8081:8081"
    networks:
      - microservices-net

  department-service:
    image: department-service
    build:
      context: ./department-service
    ports:
      - "8082:8082"
    networks:
      - microservices-net

networks:
  microservices-net:
    driver: bridge

Explanation:

  • services: Defines both employee-service and department-service.
  • networks: Creates a bridge network for service communication.

Step 6: Run Docker Compose

Navigate to the directory with the docker-compose.yml file and run:

docker-compose up --build

Docker Compose will build the images and start the containers.


Step 7: Test Microservices Communication

Use Postman or a browser to test the endpoints:

The response from department-service should include employee details fetched from employee-service.


Conclusion

You’ve successfully set up two Spring Boot microservices, containerized them using Docker, and deployed them with Docker Compose. This structure is scalable and allows for independent updates to each service, making it easier to maintain a microservices architecture.

Next Steps:

  • Add databases (e.g., MySQL) to the services.
  • Implement service discovery using Spring Cloud.

Comments