0% found this document useful (0 votes)
2 views5 pages

Java Microservices Part3 Data Resilience

This document serves as a practical guide for engineers transitioning from monolithic Spring Boot applications to a microservices architecture, focusing on database-per-service, the saga pattern for data consistency, and resilience strategies like circuit breakers. It emphasizes the importance of service isolation, the need for strategies to maintain data consistency across services, and the implementation of resilience patterns to handle network failures. The next part will address deploying these services using Docker and Kubernetes, along with monitoring techniques for distributed systems.

Uploaded by

shakti.m.patole
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views5 pages

Java Microservices Part3 Data Resilience

This document serves as a practical guide for engineers transitioning from monolithic Spring Boot applications to a microservices architecture, focusing on database-per-service, the saga pattern for data consistency, and resilience strategies like circuit breakers. It emphasizes the importance of service isolation, the need for strategies to maintain data consistency across services, and the implementation of resilience patterns to handle network failures. The next part will address deploying these services using Docker and Kubernetes, along with monitoring techniques for distributed systems.

Uploaded by

shakti.m.patole
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Java Microservices — Part 3 of 4

Java Microservices: Data &


Resilience
Database-per-service, the saga pattern, and circuit breakers

A practical guide for engineers moving from monolithic Spring Boot applications to a microservices
architecture, written for hands-on project work.
1. Database Per Service
In a monolith, every module reaches into the same database and can join across tables freely. In
microservices, each service owns its own database — and nothing else is allowed to touch it directly.
Other services only see that data through the owning service's API.

Figure 1.1 — Each service persists to its own database; no service reaches directly into another service's tables.

This is the single biggest mental shift coming from a monolith. It buys you independent schema evolution
and true service isolation, but it means you give up cross-service SQL joins and ACID transactions that
used to be trivial. Two consequences follow directly: you need a strategy for keeping data consistent
across services (the saga pattern, below), and you need a strategy for querying data that spans services
(typically a read-optimized view built from events, sometimes called CQRS — out of scope here, but worth
knowing the name for when you hit the need).

2. Keeping Data Consistent: The Saga Pattern


Placing an order might need to: create the order, reserve inventory, and charge the customer — three
services, three separate databases, no single transaction that spans all of them. A saga breaks this into a
sequence of local transactions, one per service, where each step publishes an event that triggers the next
step. If a step fails, previously completed steps are undone with compensating transactions rather than
a database rollback.
Figure 2.1 — A choreography-based saga: each service reacts to the previous service's event and, on failure,
compensating events unwind completed steps.

2.1 Example: compensating a failed payment


@KafkaListener(topics = "payment-failed", groupId = "inventory-service")
public void handlePaymentFailed(PaymentFailedEvent event) {
// Compensating transaction: release the stock we reserved earlier
[Link]([Link]());
}

@KafkaListener(topics = "payment-failed", groupId = "order-service")


public void handlePaymentFailedForOrder(PaymentFailedEvent event) {
[Link]([Link]());
}

Two flavors of saga exist: choreography, shown above, where services react to each other's events with
no central coordinator, and orchestration, where a dedicated saga orchestrator explicitly tells each
service what to do next. Choreography is simpler to start with; orchestration becomes worth it once a
workflow has enough steps that tracing the event chain by hand gets hard.

3. Resilience: Circuit Breakers and Retries


Network calls fail. In a monolith, a slow database query slows down one request. In microservices, a slow
or failing downstream service can exhaust threads in every service that calls it, cascading a single failure
across the whole system. Resilience4j's circuit breaker prevents this by tracking failures and, past a
threshold, failing fast instead of waiting on a service that's already struggling.
Figure 3.1 — A circuit breaker's three states: CLOSED (normal), OPEN (fail fast without calling downstream), and
HALF-OPEN (cautiously testing recovery).

3.1 Applying a circuit breaker


@Service
public class InventoryClient {

private final RestClient restClient;

@CircuitBreaker(name = "inventoryService", fallbackMethod = "stockFallback")


@Retry(name = "inventoryService")
public StockLevel getStock(String productId) {
return [Link]()
.uri("/api/stock/{productId}", productId)
.retrieve()
.body([Link]);
}

private StockLevel stockFallback(String productId, Throwable t) {


return [Link](productId);
}
}

3.2 Configuration
resilience4j:
circuitbreaker:
instances:
inventoryService:
sliding-window-size: 20
failure-rate-threshold: 50
wait-duration-in-open-state: 10s
permitted-number-of-calls-in-half-open-state: 3
retry:
instances:
inventoryService:
max-attempts: 3
wait-duration: 500ms

A useful habit: every outgoing call to another service should have a timeout, a retry policy, and a circuit
breaker with a sane fallback. Skipping this is the most common cause of a small hiccup in one service
turning into a full outage across all of them.

What's next
Part 4 covers running these services for real: containerizing them with Docker, deploying to Kubernetes,
and getting visibility into a distributed system with centralized logging, metrics, and tracing.

You might also like