Microservices Architecture with Java
A hands-on tutorial covering the implementation, containerization, and deployment of an e-commerce microservices system
Overview
This tutorial demonstrates the implementation of a complete e-commerce microservices system with the following components:
- 6 Microservices: Config Server, Eureka Server, API Gateway, Product Service, Order Service, and Inventory Service
- Service Discovery: Automatic service registration and discovery with Netflix Eureka
- Centralized Configuration: External configuration management with Spring Cloud Config
- API Gateway: Single entry point with intelligent routing using Spring Cloud Gateway
- Async Messaging: Event-driven architecture with RabbitMQ for inter-service communication
- Resilience Patterns: Circuit breakers, retries, and fallbacks with Resilience4j
- Containerization: Docker containers for each service
- Cloud Deployment: Deploy to free cloud platforms (Railway.app, Google Kubernetes Engine)
Prerequisites
Install Java 17
Windows (using Chocolatey):
# Install Chocolatey (PowerShell as Administrator)
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# Install Java 17
choco install openjdk17
# Verify installation
java -version
macOS (using Homebrew):
# Install Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install Java 17
brew install openjdk@17
# Verify
java -version
Linux (Ubuntu/Debian):
sudo apt update
sudo apt install openjdk-17-jdk -y
# Set JAVA_HOME
echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' >> ~/.bashrc
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
# Verify
java -version
Install Maven
# Windows
choco install maven
# macOS
brew install maven
# Linux (Ubuntu)
sudo apt install maven -y
# Verify
mvn -version
Install Docker Desktop
- Windows/Mac: Download from docker.com/products/docker-desktop
- Linux: Follow instructions at docs.docker.com/engine/install
# Verify Docker installation
docker --version
docker-compose --version
IDE Setup
IntelliJ IDEA Community Edition
- Download from: jetbrains.com/idea/download
- Install Spring Boot plugin
- Install Docker plugin
Cloud Deployment Options
The following platforms offer free tiers suitable for deploying and testing microservices:
Railway.app
$5 free credit per month, GitHub integration with auto-deploy, built-in PostgreSQL and RabbitMQ templates. No credit card required to start.
Sign up: railway.app
Render.com
Free tier for web services, auto-deploy from Git, built-in PostgreSQL, free SSL certificates.
Sign up: render.com
Google Cloud Platform (GKE)
$300 credit for 90 days (new accounts), always-free tier includes Compute Engine, full Kubernetes (GKE) support.
Sign up: cloud.google.com/free
AWS Free Tier
12 months free tier, 750 hours/month EC2, ECS and EKS for containers, RDS for databases.
Sign up: aws.amazon.com/free
Microsoft Azure
$200 credit for 30 days, 12 months of free services, Azure Kubernetes Service (AKS), Container Apps for serverless containers.
Sign up: azure.microsoft.com/free
Project Architecture
The system consists of the following services:
| Service | Port | Purpose | Database |
|---|---|---|---|
| Config Server | 8888 | Centralized configuration management | - |
| Eureka Server | 8761 | Service discovery and registration | - |
| API Gateway | 8080 | Single entry point, routing | - |
| Product Service | 8081 | Manage product catalog | PostgreSQL |
| Order Service | 8082 | Handle order processing | PostgreSQL |
| Inventory Service | 8083 | Track stock levels | PostgreSQL |
Technologies:
- Java 17 with Spring Boot 3.2.1
- Spring Cloud 2023.0.0 (Config, Eureka, Gateway)
- PostgreSQL for data persistence
- RabbitMQ for async messaging
- Docker for containerization
- Resilience4j for circuit breakers
Step 1: Project Setup
Create Project Directory
mkdir microservices-ecommerce
cd microservices-ecommerce
git init
# Create .gitignore
cat > .gitignore << 'EOF'
target/
*.iml
.idea/
*.class
.DS_Store
EOF
Parent POM Configuration
Create: pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.1</version>
</parent>
<groupId>com.ecommerce</groupId>
<artifactId>microservices-parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<modules>
<module>config-server</module>
<module>eureka-server</module>
<module>api-gateway</module>
<module>product-service</module>
<module>order-service</module>
<module>inventory-service</module>
</modules>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.0</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Step 2: Config Server
The Config Server provides centralized configuration management for all microservices.
Create Module
mkdir -p config-server/src/main/java/com/ecommerce/config
mkdir -p config-server/src/main/resources/config
POM Configuration
Create: config-server/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ecommerce</groupId>
<artifactId>microservices-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>config-server</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
</project>
Main Application Class
Create: config-server/src/main/java/com/ecommerce/config/ConfigServerApplication.java
package com.ecommerce.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
Configuration
Create: config-server/src/main/resources/application.yml
server:
port: 8888
spring:
application:
name: config-server
cloud:
config:
server:
native:
search-locations: classpath:/config
profiles:
active: native
Step 3: Eureka Server
Eureka Server handles service discovery - services automatically register and find each other.
Create Module
mkdir -p eureka-server/src/main/java/com/ecommerce/eureka
mkdir -p eureka-server/src/main/resources
POM Configuration
Create: eureka-server/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ecommerce</groupId>
<artifactId>microservices-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>eureka-server</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
Main Application Class
Create: eureka-server/src/main/java/com/ecommerce/eureka/EurekaServerApplication.java
package com.ecommerce.eureka;
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);
}
}
Configuration
Create: eureka-server/src/main/resources/application.yml
server:
port: 8761
spring:
application:
name: eureka-server
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka/
Step 4: Product Service
Our first business microservice - manages the product catalog.
Create Module Structure
mkdir -p product-service/src/main/java/com/ecommerce/product/{model,repository,service,controller}
mkdir -p product-service/src/main/resources
POM Configuration
Create: product-service/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ecommerce</groupId>
<artifactId>microservices-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>product-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
Product Entity
Create: product-service/src/main/java/com/ecommerce/product/model/Product.java
package com.ecommerce.product.model;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Entity
@Table(name = "products")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
private String description;
@Column(nullable = false)
private BigDecimal price;
private String category;
}
Repository
Create: product-service/src/main/java/com/ecommerce/product/repository/ProductRepository.java
package com.ecommerce.product.repository;
import com.ecommerce.product.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByCategory(String category);
List<Product> findByNameContainingIgnoreCase(String name);
}
Service Layer
Create: product-service/src/main/java/com/ecommerce/product/service/ProductService.java
package com.ecommerce.product.service;
import com.ecommerce.product.model.Product;
import com.ecommerce.product.repository.ProductRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository productRepository;
public List<Product> getAllProducts() {
return productRepository.findAll();
}
public Product getProductById(Long id) {
return productRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Product not found"));
}
public Product createProduct(Product product) {
return productRepository.save(product);
}
public List<Product> searchProducts(String query) {
return productRepository.findByNameContainingIgnoreCase(query);
}
}
REST Controller
Create: product-service/src/main/java/com/ecommerce/product/controller/ProductController.java
package com.ecommerce.product.controller;
import com.ecommerce.product.model.Product;
import com.ecommerce.product.service.ProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor
public class ProductController {
private final ProductService productService;
@GetMapping
public List<Product> getAllProducts() {
return productService.getAllProducts();
}
@GetMapping("/{id}")
public Product getProduct(@PathVariable Long id) {
return productService.getProductById(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Product createProduct(@RequestBody Product product) {
return productService.createProduct(product);
}
@GetMapping("/search")
public List<Product> searchProducts(@RequestParam String q) {
return productService.searchProducts(q);
}
}
Main Application
Create: product-service/src/main/java/com/ecommerce/product/ProductServiceApplication.java
package com.ecommerce.product;
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);
}
}
Bootstrap Configuration
Create: product-service/src/main/resources/application.yml
spring:
application:
name: product-service
config:
import: optional:configserver:http://localhost:8888
Service Configuration (in Config Server)
Create: config-server/src/main/resources/config/product-service.yml
server:
port: 8081
spring:
datasource:
url: jdbc:postgresql://localhost:5432/productdb
username: postgres
password: postgres
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
Step 5: Order Service with Circuit Breaker
The Order Service demonstrates inter-service communication and resilience patterns.
Add Circuit Breaker Dependency
In order-service/pom.xml, add:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
Order Entity
Create: order-service/src/main/java/com/ecommerce/order/model/Order.java
package com.ecommerce.order.model;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Table(name = "orders")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long productId;
private Integer quantity;
private BigDecimal totalAmount;
@Enumerated(EnumType.STRING)
private OrderStatus status;
private LocalDateTime createdAt;
}
enum OrderStatus {
PENDING, CONFIRMED, FAILED
}
Service with Circuit Breaker
Create: order-service/src/main/java/com/ecommerce/order/service/OrderService.java
package com.ecommerce.order.service;
import com.ecommerce.order.model.*;
import com.ecommerce.order.repository.OrderRepository;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime;
@Service
@RequiredArgsConstructor
@Slf4j
public class OrderService {
private final OrderRepository orderRepository;
private final RestTemplate restTemplate;
@CircuitBreaker(name = "productService", fallbackMethod = "createOrderFallback")
public Order createOrder(Order order) {
// Verify product exists (calls Product Service)
String url = "http://product-service/api/products/" + order.getProductId();
restTemplate.getForObject(url, Object.class);
order.setStatus(OrderStatus.PENDING);
order.setCreatedAt(LocalDateTime.now());
return orderRepository.save(order);
}
private Order createOrderFallback(Order order, Exception ex) {
log.error("Circuit breaker activated", ex);
Order fallbackOrder = new Order();
fallbackOrder.setStatus(OrderStatus.FAILED);
fallbackOrder.setProductId(order.getProductId());
fallbackOrder.setQuantity(order.getQuantity());
return fallbackOrder;
}
}
RestTemplate Configuration
Create: order-service/src/main/java/com/ecommerce/order/config/RestTemplateConfig.java
package com.ecommerce.order.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Circuit Breaker Configuration
Add to config-server/src/main/resources/config/order-service.yml:
resilience4j:
circuitbreaker:
instances:
productService:
sliding-window-size: 10
failure-rate-threshold: 50
wait-duration-in-open-state: 10s
permitted-number-of-calls-in-half-open-state: 3
Step 6: Event-Driven Communication with RabbitMQ
Connect Order Service and Inventory Service through async messaging.
RabbitMQ Configuration (Order Service)
Create: order-service/src/main/java/com/ecommerce/order/config/RabbitMQConfig.java
package com.ecommerce.order.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
@Bean
public TopicExchange orderExchange() {
return new TopicExchange("order-exchange");
}
@Bean
public Queue inventoryQueue() {
return new Queue("inventory-queue");
}
@Bean
public Binding inventoryBinding(Queue inventoryQueue, TopicExchange orderExchange) {
return BindingBuilder.bind(inventoryQueue)
.to(orderExchange)
.with("order.placed");
}
}
Event Publisher
Create: order-service/src/main/java/com/ecommerce/order/event/OrderEventPublisher.java
package com.ecommerce.order.event;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class OrderEventPublisher {
private final RabbitTemplate rabbitTemplate;
public void publishOrderPlaced(OrderEvent event) {
event.setEventType("ORDER_PLACED");
rabbitTemplate.convertAndSend("order-exchange", "order.placed", event);
}
}
Event Listener (Inventory Service)
Create: inventory-service/src/main/java/com/ecommerce/inventory/event/OrderEventListener.java
package com.ecommerce.inventory.event;
import com.ecommerce.inventory.service.InventoryService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
@Slf4j
public class OrderEventListener {
private final InventoryService inventoryService;
@RabbitListener(queues = "inventory-queue")
public void handleOrderPlaced(OrderEvent event) {
log.info("Received order event: {}", event);
try {
inventoryService.reduceStock(event.getProductId(), event.getQuantity());
log.info("Stock reduced successfully");
} catch (Exception e) {
log.error("Failed to reduce stock", e);
}
}
}
Step 7: API Gateway
Create a single entry point for all microservices.
POM Configuration
Create: api-gateway/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ecommerce</groupId>
<artifactId>microservices-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>api-gateway</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
Configuration with Routes
Create: api-gateway/src/main/resources/application.yml
server:
port: 8080
spring:
application:
name: api-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: product-service
uri: lb://product-service
predicates:
- Path=/api/products/**
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
- id: inventory-service
uri: lb://inventory-service
predicates:
- Path=/api/inventory/**
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
Step 8: Dockerization
Containerize all services for consistent deployment.
Dockerfile Template
Create this Dockerfile in each service directory:
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# Add non-root user
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
# Copy JAR file
COPY target/*.jar app.jar
RUN chown -R appuser:appuser /app
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s \
CMD wget --no-verbose --tries=1 --spider \
http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", \
"-XX:MaxRAMPercentage=75.0", \
"-Djava.security.egd=file:/dev/./urandom", \
"-jar", "app.jar"]
Docker Compose
Create: docker-compose.yml (project root)
version: '3.8'
services:
postgres-product:
image: postgres:15-alpine
environment:
POSTGRES_DB: productdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- product-data:/var/lib/postgresql/data
postgres-order:
image: postgres:15-alpine
environment:
POSTGRES_DB: orderdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5433:5432"
volumes:
- order-data:/var/lib/postgresql/data
postgres-inventory:
image: postgres:15-alpine
environment:
POSTGRES_DB: inventorydb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5434:5432"
volumes:
- inventory-data:/var/lib/postgresql/data
rabbitmq:
image: rabbitmq:3-management-alpine
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
volumes:
product-data:
order-data:
inventory-data:
Step 9: Build and Run Locally
Build All Services
# From project root
mvn clean package -DskipTests
Start Infrastructure
# Start databases and RabbitMQ
docker-compose up -d
# Wait for services to start
sleep 10
Start Services (Terminal Tabs)
# Terminal 1: Config Server
cd config-server
mvn spring-boot:run
# Terminal 2: Eureka Server (wait 15s after config server)
cd eureka-server
mvn spring-boot:run
# Terminal 3: Product Service (wait 20s after eureka)
cd product-service
mvn spring-boot:run
# Terminal 4: Order Service
cd order-service
mvn spring-boot:run
# Terminal 5: Inventory Service
cd inventory-service
mvn spring-boot:run
# Terminal 6: API Gateway
cd api-gateway
mvn spring-boot:run
Verify Services
- Eureka Dashboard:
http://localhost:8761 - RabbitMQ Management:
http://localhost:15672(guest/guest) - API Gateway:
http://localhost:8080
Test the System
# Create a product
curl -X POST http://localhost:8080/api/products \
-H "Content-Type: application/json" \
-d '{
"name": "Laptop",
"description": "High-performance laptop",
"price": 999.99,
"category": "Electronics"
}'
# Get all products
curl http://localhost:8080/api/products
# Create inventory entry
curl -X POST http://localhost:8083/api/inventory \
-H "Content-Type: application/json" \
-d '{
"productId": 1,
"quantity": 100
}'
# Create an order (triggers event to inventory)
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{
"productId": 1,
"quantity": 2,
"totalAmount": 1999.98
}'
Step 10: Deploy to Railway.app
Install Railway CLI
# macOS/Linux
curl -fsSL https://railway.app/install.sh | sh
# Windows (PowerShell)
iwr https://railway.app/install.ps1 | iex
# Login
railway login
Create Railway Project
# Initialize project
railway init
# Link to your Railway account
railway link
Add Databases
- Go to Railway Dashboard (
https://railway.app) - Click "New" → "Database" → "PostgreSQL" (do this 3 times for product, order, inventory DBs)
- Click "New" → "Template" → Search "RabbitMQ" → Deploy
Update Configuration
Use environment variables in your application.yml:
spring:
datasource:
url: ${DATABASE_URL}
rabbitmq:
addresses: ${RABBITMQ_URL}
Deploy Services
# Deploy each service
cd config-server
railway up
cd ../eureka-server
railway up
cd ../product-service
railway up
# Continue for all services...
Alternative: GitHub Integration
- Push your code to GitHub
- In Railway: "New" → "Deploy from GitHub repo"
- Select your repository
- Railway auto-deploys on git push!
Step 11: Deploy to Kubernetes (Optional)
Setup Google Kubernetes Engine
# Install Google Cloud SDK
# macOS: brew install google-cloud-sdk
# Linux: curl https://sdk.cloud.google.com | bash
# Login and setup
gcloud init
gcloud auth login
# Set project
export PROJECT_ID=your-project-id
gcloud config set project $PROJECT_ID
# Enable APIs
gcloud services enable container.googleapis.com
gcloud services enable artifactregistry.googleapis.com
# Create GKE cluster (autopilot mode)
gcloud container clusters create-auto ecommerce-cluster \
--region=us-central1
# Get credentials
gcloud container clusters get-credentials ecommerce-cluster \
--region=us-central1
Build and Push Docker Images
# Create Artifact Registry repository
gcloud artifacts repositories create ecommerce-repo \
--repository-format=docker \
--location=us-central1
# Configure Docker
gcloud auth configure-docker us-central1-docker.pkg.dev
# Build and push images
export REPO=us-central1-docker.pkg.dev/$PROJECT_ID/ecommerce-repo
# For each service:
cd config-server
mvn clean package -DskipTests
docker build -t $REPO/config-server:1.0 .
docker push $REPO/config-server:1.0
# Repeat for all services...
Create Kubernetes Manifests
Create: k8s/product-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-service
spec:
replicas: 2
selector:
matchLabels:
app: product-service
template:
metadata:
labels:
app: product-service
spec:
containers:
- name: product-service
image: us-central1-docker.pkg.dev/PROJECT_ID/ecommerce-repo/product-service:1.0
ports:
- containerPort: 8081
env:
- name: SPRING_DATASOURCE_URL
value: jdbc:postgresql://postgres-service:5432/productdb
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health
port: 8081
initialDelaySeconds: 60
readinessProbe:
httpGet:
path: /actuator/health
port: 8081
initialDelaySeconds: 30
---
apiVersion: v1
kind: Service
metadata:
name: product-service
spec:
selector:
app: product-service
ports:
- port: 80
targetPort: 8081
type: ClusterIP
Deploy to Kubernetes
# Apply all manifests
kubectl apply -f k8s/
# Check deployments
kubectl get deployments
kubectl get pods
kubectl get services
# View logs
kubectl logs -f deployment/product-service
# Access via port-forward (testing)
kubectl port-forward service/api-gateway 8080:80
Summary
- Built 6 production-ready microservices with Spring Boot and Spring Cloud
- Implemented service discovery with Netflix Eureka
- Created centralized configuration with Spring Cloud Config
- Built an API Gateway for intelligent routing
- Implemented circuit breakers for resilience with Resilience4j
- Created event-driven architecture with RabbitMQ
- Containerized services with Docker
- Deployed to cloud platforms (Railway.app and GKE)
- Understood microservices architecture patterns and best practices
Next Steps
Enhance Your System:
- Security: Add JWT authentication and Spring Security
- Monitoring: Implement Prometheus and Grafana
- Distributed Tracing: Add Sleuth and Zipkin
- API Documentation: Integrate OpenAPI/Swagger
- Caching: Add Redis for performance
- Testing: Write integration and contract tests
- CI/CD: Set up GitHub Actions or Jenkins
Practice Exercises:
- Add Payment Service: Create a new microservice that processes payments and participates in the SAGA pattern
- Implement SAGA: Add compensating transactions for order flow (order → inventory → payment → confirmation)
- Add Rate Limiting: Implement request throttling in the API Gateway
- Add Distributed Tracing: Integrate Zipkin to trace requests across services
Advanced Topics:
- Service Mesh (Istio, Linkerd)
- Event Sourcing and CQRS
- Chaos Engineering with Chaos Monkey
- Blue-Green Deployments
- Domain-Driven Design (DDD)
Additional Resources
Official Documentation:
- Spring Cloud Documentation
- Spring Boot Documentation
- Kubernetes Documentation
- Docker Getting Started
Books:
- Building Microservices by Sam Newman (O'Reilly)
- Microservices Patterns by Chris Richardson
- Spring Microservices in Action by John Carnell
Online Learning:
- Microservices.io - Patterns catalog
- The Twelve-Factor App - Best practices
- Kubernetes Tutorials
Community:
- Spring Community Forums
- Stack Overflow (tags: spring-boot, spring-cloud, microservices)
- Reddit: r/microservices, r/java, r/kubernetes