How to Create Microservices in Java Using Spring Boot

Microservices architecture is a design approach where an application is composed of small, loosely coupled services that communicate with each other. In this tutorial, we'll create a simple e-commerce application using a microservices architecture with Spring Boot. We'll cover the following components:
  1. Eureka Server: For service discovery.
  2. API Gateway: Acts as a single entry point.
  3. Product Service: Manages product information.
  4. Order Service: Manages customer orders.
  5. Inventory Service: Manages product inventory.

Prerequisites

  • JDK 17 or later
  • Maven or Gradle
  • Docker (optional, for running supporting services like Eureka, RabbitMQ, etc.)
  • IDE (IntelliJ IDEA, Eclipse, etc.)

Step 1: Set Up the Eureka Server for Service Discovery

1.1 Create the Project

Use Spring Initializr to create a new project with the following dependencies:

  • Eureka Server

1.2 Configure application.properties

Set up the application properties for the Eureka Server.

server.port=8761
spring.application.name=eureka-server
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

1.3 Enable Eureka Server

Add the @EnableEurekaServer annotation to the main application class.

package com.example.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

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

Step 2: Set Up API Gateway

2.1 Create the Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Cloud Gateway
  • Eureka Discovery Client

2.2 Configure application.properties

Set up the application properties for the API Gateway.

server.port=8080
spring.application.name=api-gateway
eureka.client.service-url.default-zone=http://localhost:8761/eureka/
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true

2.3 Enable Eureka Client and Gateway

Add the @EnableDiscoveryClient annotation in the main application class.

package com.example.apigateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

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

Step 3: Set Up Product Service

3.1 Create the Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Web
  • Spring Boot Actuator
  • Spring Data JPA
  • H2 Database
  • Eureka Discovery Client

3.2 Configure application.properties

Set up the application properties for the Product Service.

server.port=8081
spring.application.name=product-service
eureka.client.service-url.default-zone=http://localhost:8761/eureka/
spring.datasource.url=jdbc:h2:mem:productdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.h2.console.enabled=true
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

3.3 Enable Eureka Client

Add the @EnableDiscoveryClient annotation to the main application class.

package com.example.productservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

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

3.4 Create Product Model

Create a simple Product model.

package com.example.productservice;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Product {
    @Id
    private String id;
    private String name;
    private double price;

    // Getters and setters
}

3.5 Create Product Repository

Create a repository interface to manage Product entities.

package com.example.productservice;

import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductRepository extends JpaRepository<Product, String> {
}

3.6 Create Product Service

Create a service to handle product-related business logic.

package com.example.productservice;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    public List<Product> getAllProducts() {
        return productRepository.findAll();
    }

    public Product getProductById(String id) {
        return productRepository.findById(id).orElse(null);
    }

    public Product addProduct(Product product) {
        return productRepository.save(product);
    }
}

3.7 Create Product Controller

Create a controller to handle HTTP requests.

package com.example.productservice;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/products")
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping
    public List<Product> getAllProducts() {
        return productService.getAllProducts();
    }

    @GetMapping("/{id}")
    public Product getProductById(@PathVariable String id) {
        return productService.getProductById(id);
    }

    @PostMapping
    public Product addProduct(@RequestBody Product product) {
        return productService.addProduct(product);
    }
}

Step 4: Set Up Order Service

4.1 Create the Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Web
  • Spring Boot Actuator
  • Spring Data JPA
  • H2 Database
  • Eureka Discovery Client
  • OpenFeign

4.2 Configure application.properties

Set up the application properties for the Order Service.

server.port=8082
spring.application.name=order-service
eureka.client.service-url.default-zone=http://localhost:8761/eureka/
spring.datasource.url=jdbc:h2:mem:orderdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.h2.console.enabled=true
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

4.3 Enable Eureka Client and Feign Clients

Add the @EnableDiscoveryClient and @EnableFeignClients annotations to the main application class.

package com.example.orderservice;

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

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

4.4 Create Order Model

Create a simple Order model.

package com.example.orderservice;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Order {
    @Id
    private String id;
    private String productId;
    private int quantity;

    // Getters and setters
}

4.5 Create Order Repository

Create a repository interface to manage Order entities.

package com.example.orderservice;

import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderRepository extends JpaRepository<Order, String> {
}

4.6 Create Product Client

Create a Feign client interface to communicate with 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")
public interface ProductClient {

    @GetMapping("/products/{id}")
    Product getProductById(@PathVariable String id);
}

4.7 Create Product DTO

Create a Product DTO to match the one in product-service.

package com.example.orderservice;

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

    // Getters and setters
}

4.8 Create Order Service

Create a service to handle order-related business logic.

package com.example.orderservice;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private ProductClient productClient;

    public List<Order> getAllOrders() {
        return orderRepository.findAll();
    }

    public Order getOrderById(String id) {
        return orderRepository.findById(id).orElse(null);
    }

    public Order addOrder(Order order) {
        Product product = productClient.getProductById(order.getProductId());
        if (product != null) {
            return orderRepository.save(order);
        } else {
            throw new RuntimeException("Product not found");
        }
    }
}

4.9 Create Order Controller

Create a controller to handle HTTP requests.

package com.example.orderservice;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/orders")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @GetMapping
    public List<Order> getAllOrders() {
        return orderService.getAllOrders();
    }

    @GetMapping("/{id}")
    public Order getOrderById(@PathVariable String id) {
        return orderService.getOrderById(id);
    }

    @PostMapping
    public Order addOrder(@RequestBody Order order) {
        return orderService.addOrder(order);
    }
}

Step 5: Set Up Inventory Service

5.1 Create the Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Web
  • Spring Boot Actuator
  • Spring Data JPA
  • H2 Database
  • Eureka Discovery Client

5.2 Configure application.properties

Set up the application properties for the Inventory Service.

server.port=8083
spring.application.name=inventory-service
eureka.client.service-url.default-zone=http://localhost:8761/eureka/
spring.datasource.url=jdbc:h2:mem:inventorydb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.h2.console.enabled=true
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

5.3 Enable Eureka Client

Add the @EnableDiscoveryClient annotation to the main application class.

package com.example.inventoryservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

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

5.4 Create Inventory Model

Create a simple Inventory model.

package com.example.inventoryservice;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Inventory {
    @Id
    private String productId;
    private int quantity;

    // Getters and setters
}

5.5 Create Inventory Repository

Create a repository interface to manage Inventory entities.

package com.example.inventoryservice;

import org.springframework.data.jpa.repository.JpaRepository;

public interface InventoryRepository extends JpaRepository<Inventory, String> {
}

5.6 Create Inventory Service

Create a service to handle inventory-related business logic.

package com.example.inventoryservice;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class InventoryService {

    @Autowired
    private InventoryRepository inventoryRepository;

    public List<Inventory> getAllInventory() {
        return inventoryRepository.findAll();
    }

    public Inventory getInventoryByProductId(String productId) {
        return inventoryRepository.findById(productId).orElse(null);
    }

    public Inventory updateInventory(String productId, int quantity) {
        Inventory inventory = inventoryRepository.findById(productId).orElseThrow(() -> new RuntimeException("Product not found"));
        inventory.setQuantity(quantity);
        return inventoryRepository.save(inventory);
    }
}

5.7 Create Inventory Controller

Create a controller to handle HTTP requests.

package com.example.inventoryservice;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/inventory")
public class InventoryController {

    @Autowired
    private InventoryService inventoryService;

    @GetMapping
    public List<Inventory> getAllInventory() {
        return inventoryService.getAllInventory();
    }

    @GetMapping("/{productId}")
    public Inventory getInventoryByProductId(@PathVariable String productId) {
        return inventoryService.getInventoryByProductId(productId);
    }

    @PutMapping("/{productId}")
    public Inventory updateInventory(@PathVariable String productId, @RequestParam int quantity) {
        return inventoryService.updateInventory(productId, quantity);
    }
}

Step 6: Run the Services

  1. Start the Eureka Server: Run the EurekaServerApplication class.
  2. Start the API Gateway: Run the ApiGatewayApplication class.
  3. Start the Product Service: Run the ProductServiceApplication class.
  4. Start the Order Service: Run the OrderServiceApplication class.
  5. Start the Inventory Service: Run the InventoryServiceApplication class.

Step 7: Test the E-Commerce Application

  1. Test the Product Service:

    • Add products using POST /products:
      {
        "id": "1",
        "name": "Product 1",
        "price": 100.0
      }
      
    • Retrieve products using GET /products.
  2. Test the Order Service:

    • Create orders using POST /orders:
      {
        "id": "1",
        "productId": "1",
        "quantity": 1
      }
      
    • Retrieve orders using GET /orders.
  3. Test the Inventory Service:

    • Update inventory using PUT /inventory/{productId}?quantity=10.
    • Retrieve inventory using GET /inventory.

Conclusion

You have successfully set up a basic e-commerce application using Spring Boot microservices architecture. This setup includes a Product Service, Order Service, Inventory Service, Eureka Server for service discovery, and an API Gateway. This example can be expanded with more features such as user management, payment processing, and more complex business logic.

Comments