Spring Boot Microservices with Docker Compose Example

In this tutorial, we will:

  • Create two microservices using Spring Boot: employee-service and department-service.
  • Containerize both services using Docker.
  • Use Docker Compose to orchestrate the deployment of these microservices.

What You'll Learn:

  • How to use Docker Compose to define and run multi-container Docker applications.
  • How to set up communication between multiple Spring Boot microservices.
Spring Boot Microservices with Docker Compose Example

Introduction to Docker Compose

Docker Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application's services, networks, and volumes. Then, with a single command, you can create and start all the services from your configuration.

Prerequisites

Before starting, ensure you have the following:

  • JDK 17 or later
  • Maven or Gradle
  • Docker and Docker Compose
  • 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 data.
  2. department-service: Manages department data and fetches employee details from the employee-service.

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

Set up your application.properties for employee-service:

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

2.3 Create the Employee Model

Define an Employee model:

package com.example.employeeservice;

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

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

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

2.5 Create Dockerfile for employee-service

In the root directory of the project, create a Dockerfile:

# 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

Configure your application.properties for department-service:

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

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

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 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:

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 REST 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 Dockerfile for department-service

In the root directory of the project, create a Dockerfile:

# 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"]

Step 4: Create Docker Compose File

In the root directory of your project, create a docker-compose.yml file. This file will define both employee-service and department-service, as well as the network for communication between them.

version: '3'
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"
    environment:
      - EMPLOYEE_SERVICE_URL=http://employee-service:8081
    networks:
      - microservices-net

networks:
  microservices-net:
    driver: bridge

Explanation:

  • employee-service and department-service: Both services are defined in this file with build contexts and ports.
  • networks: A custom network microservices-net is created so the services can communicate with each other.

Step 5: Build and Run Docker Compose

In the root directory of your project (where the docker-compose.yml file is located), run the following command to build and start the services:

docker-compose up --build

This command will:

  • Build the Docker images for employee-service and department-service.
  • Start both services and connect them through the custom Docker network.

Step 6: Test the Microservices

Once the services are running, you can test them using a browser or Postman.

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

Conclusion

You have successfully created and deployed two Spring Boot microservices (employee-service and department-service) using Docker Compose. This architecture allows for easy scaling and isolation of services, making it perfect for microservices applications.

Next Steps:

  • Add database integration (e.g., MySQL) to the services.
  • Implement service discovery using tools like Spring Cloud Netflix Eureka.

Comments