DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Distributed Task Synchronization: Leveraging ShedLock in Spring
  • A New Era Of Spring Cloud
  • Failure Handling Mechanisms in Microservices and Their Importance
  • Chaos Engineering for Microservices

Trending

  • Transforming AI-Driven Data Analytics with DeepSeek: A New Era of Intelligent Insights
  • Why High-Performance AI/ML Is Essential in Modern Cybersecurity
  • Docker Model Runner: Streamlining AI Deployment for Developers
  • A Modern Stack for Building Scalable Systems
  1. DZone
  2. Software Design and Architecture
  3. Microservices
  4. How to Implement Specific Distributed System Patterns Using Spring Boot: Introduction

How to Implement Specific Distributed System Patterns Using Spring Boot: Introduction

This article will focus on the specific recommendations for implementing various distributed system patterns regarding Spring Boot.

By 
Dhruv Seth user avatar
Dhruv Seth
·
Updated by 
Karan Kumar Ratra user avatar
Karan Kumar Ratra
·
Sep. 11, 24 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
9.3K Views

Join the DZone community and get the full member experience.

Join For Free

Regarding contemporary software architecture, distributed systems have been widely recognized for quite some time as the foundation for applications with high availability, scalability, and reliability goals. When systems shifted from a centralized structure, it became increasingly important to focus on the components and architectures that support a distributed structure. Regarding the choice of frameworks, Spring Boot is a widely adopted framework encompassing many tools, libraries, and components to support these patterns. This article will focus on the specific recommendations for implementing various distributed system patterns regarding Spring Boot, backed by sample code and professional advice.

Spring Boot Overview

One of the most popular Java EE frameworks for creating apps is Spring. The Spring framework offers a comprehensive programming and configuration mechanism for the Java platform. It seeks to make Java EE programming easier and increase developers' productivity in the workplace. Any type of deployment platform can use it. It tries to meet modern industry demands by making application development rapid and straightforward. While the Spring framework focuses on giving you flexibility, the goal of Spring Boot is to reduce the amount of code and give developers the most straightforward approach possible to create web applications. Spring Boot's default codes and annotation setup lessen the time it takes to design an application. It facilitates the creation of stand-alone applications with minimal, if any, configuration. It is constructed on top of a module of the Spring framework.

With its layered architecture, Spring Boot has a hierarchical structure where each layer can communicate with any layer above or below it.

  • Presentation layer: The presentation layer converts the JSON parameter to an object, processes HTTP requests (from the specific Restful API), authenticates the request, and sends it to the business layer. It is made up, in brief, of views or the frontend section.
  • Business layer: All business logic is managed by this layer. It employs services from data access layers and is composed of service classes. It also carries out validation and permission.
  • Persistence layer: Using various tools like JDBC and Repository, the persistence layer translates business objects from and to database rows. It also houses all of the storage logic.
  • Database layer: CRUD (create, retrieve, update, and delete) actions are carried out at the database layer. The actual scripts that import and export data into and out of the database

This is how the Spring Boot flow architecture appears:

Spring Boot flow architecture


Significant Differences between Spring and Spring Boot

Table 1: Significant differences between Spring and Spring Boot

1. Microservices Pattern

The pattern of implementing microservices is arguably one of the most used designs in the current software world. It entails breaking down a complex, monolithic application into a collection of small, interoperable services. System-dependent microservices execute their processes and interconnect with other services using simple, lightweight protocols, commonly RESTful APIs or message queues. The first advantages of microservices include that they are easier to scale, separate faults well, and can be deployed independently. Spring Boot and Spring Cloud provide an impressive list of features to help implement a microservices architecture. Services from Spring Cloud include service registry, provided by Netflix Eureka or Consul; configuration offered by Spring Cloud config; and resilience pattern offered through either Hystrix or recently developed Resilience4j. 

Let’s, for instance, take a case where you’re creating an e-commerce application. This application can be split into several microservices covering different domains, for example, OrderService, PaymentService, and InventoryService. All these services can be built, tested, and implemented singularly in service-oriented systems.

Java
 
@RestController

@RequestMapping("/orders")

public class OrderController {

 

    @Autowired

    private OrderService orderService;

 

    @PostMapping

    public ResponseEntity<Order> createOrder(@RequestBody Order order) {

        Order createdOrder = orderService.createOrder(order);

        return ResponseEntity.status(HttpStatus.CREATED).body(createdOrder);

    }

 

    @GetMapping("/{id}")

    public ResponseEntity<Order> getOrder(@PathVariable Long id) {

        Order order = orderService.getOrderById(id);

        return ResponseEntity.ok(order);

    }

}

 

@Service

public class OrderService {

 

    // Mocking a database call

    private Map<Long, Order> orderRepository = new HashMap<>();

 

    public Order createOrder(Order order) {

        order.setId(System.currentTimeMillis());

        orderRepository.put(order.getId(), order);

        return order;

    }

 

    public Order getOrderById(Long id) {

        return orderRepository.get(id);

    }

}


In the example above, OrderController offers REST endpoints for making and retrieving orders, while OrderService manages the business logic associated with orders. With each service operating in a separate, isolated environment, this pattern may be replicated for the PaymentService and InventoryService.

2. Event-Driven Pattern

In an event-driven architecture, the services do not interact with each other in a request-response manner but rather in a loosely coupled manner where some services only produce events and others only consume them. This pattern is most appropriate when there is a need for real-time processing while simultaneously fulfilling high scalability requirements. It thus establishes the independence of the producers and consumers of events — they are no longer tightly linked. An event-driven system can efficiently work with large and unpredictable loads of events and easily tolerate partial failures.

Implementation With Spring Boot

Apache Kafka, RabbitMQ, or AWS SNS/SQS can be effectively integrated with Spring Boot, greatly simplifying the creation of event-driven architecture. Spring Cloud Stream provides developers with a higher-level programming model oriented on microservices based on message-driven architecture, hiding the specifics of different messaging systems behind the same API. 

Let us expand more on the e-commerce application. Consider such a scenario where the order is placed, and the OrderService sends out an event. This event can be consumed by other services like InventoryService to adjust the stock automatically and by ShippingService to arrange delivery.

Java
 
// OrderService publishes an event

@Autowired

private KafkaTemplate<String, String> kafkaTemplate;

 

public void publishOrderEvent(Order order) {

    kafkaTemplate.send("order_topic", "Order created: " + order.getId());

}

 

// InventoryService listens for the order event

@KafkaListener(topics = "order_topic", groupId = "inventory_group")

public void consumeOrderEvent(String message) {

    System.out.println("Received event: " + message);

    // Update inventory based on the order details

}


In this example, OrderService publishes an event to a Kafka topic whenever a new order is created. InventoryService, which subscribes to this topic, consumes and processes the event accordingly.

3. CQRS (Command Query Responsibility Segregation)

The CQRS pattern suggests the division of the handling of commands into events that change the state from the queries, which are events that retrieve the state. This can help achieve a higher level of scalability and maintainability of the solution, especially when the read and write operations within an application are significantly different in the given area of a business domain. As for the support for implementing CQRS in Spring Boot applications, let’s mention the Axon Framework, designed to fit this pattern and includes command handling, event sourcing, and query handling into the mix. In a CQRS setup, commands modify the state in the write model, while queries retrieve data from the read model, which could be optimized for different query patterns.

A banking application, for example, where account balances are often asked, but the number of transactions that result in balance change is comparatively less. By separating these concerns, a developer can optimize the read model for fast access while keeping the write model more consistent and secure.

Java
 
// Command to handle money withdrawal

@CommandHandler

public void handle(WithdrawMoneyCommand command) {

    if (balance >= command.getAmount()) {

        balance -= command.getAmount();

        AggregateLifecycle.apply(new MoneyWithdrawnEvent(command.getAccountId(), command.getAmount()));

    } else {

        throw new InsufficientFundsException();

    }

}

 

// Query to fetch account balance

@QueryHandler

public AccountBalance handle(FindAccountBalanceQuery query) {

    return new AccountBalance(query.getAccountId(), this.balance);

}


In this code snippet, a WithdrawMoneyCommand modifies the account balance in the command model, while a FindAccountBalanceQuery retrieves the balance from the query model.

4. API Gateway Pattern

The API Gateway pattern is one of the critical patterns used in a microservices architecture. It is the central access point for every client request and forwards it to the right microservice. The following are the cross-cutting concerns: Authentication, logging, rate limiting, and load balancing, which are all handled by the gateway. Spring Cloud Gateway is considered the most appropriate among all the available options for using an API Gateway in a Spring Boot application. It is developed on Project Reactor, which makes it very fast and can work with reactive streams.

Let us go back to our first e-commerce example: an API gateway can forward the request to UserService, OrderService, PaymentService, etc. It can also have an authentication layer and accept subsequent user requests to be passed to the back-end services.

Java
 
@Bean

public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {

    return builder.routes()

        .route("order_service", r -> r.path("/orders/**")

            .uri("lb://ORDER-SERVICE"))

        .route("payment_service", r -> r.path("/payments/**")

            .uri("lb://PAYMENT-SERVICE"))

        .build();

}


In this example, the API Gateway routes requests to the appropriate microservice based on the request path. The lb://prefix indicates that these services are registered with a load balancer (such as Eureka).

5. Saga Pattern

The Saga pattern maintains transactions across multiple services in a distributed transaction environment. With multiple microservices available, it becomes challenging to adjust data consistency in a distributed system where each service can have its own database. The Saga pattern makes it possible for all the operations across services to be successfully completed or for the system to perform compensating transactions to reverse the effects of failure across services.

The Saga pattern can be implemented by Spring Boot using either choreography — where services coordinate and interact directly through events — or orchestration, where a central coordinator oversees the Saga. Each strategy has advantages and disadvantages depending on the intricacy of the transactions and the degree of service coupling. Imagine a scenario where placing an order involves multiple services: A few of them include PaymentService, InventoryService, and ShippingService. Every service has to be successfully executed for the order to be confirmed. If any service fails, compensating transactions must be performed to bring the system back to its initial status.

Java
 
public void processOrder(Order order) {

    try {

        paymentService.processPayment(order.getPaymentDetails());

        inventoryService.reserveItems(order.getItems());

        shippingService.schedule**process(order);**


Amazon’s Saga Pattern Functions Workflow

Figure 2: Amazon’s Saga Pattern Functions Workflow

The saga pattern is a failure management technique that assists in coordinating transactions across several microservices to preserve data consistency and establish consistency in distributed systems. Every transaction in a microservice publishes an event, and the subsequent transaction is started based on the event's result. Depending on whether the transactions are successful or unsuccessful, they can proceed in one of two ways.

As demonstrated in Figure 2, the Saga pattern uses AWS Step Functions to construct an order processing system. Every step (like "ProcessPayment") has a separate step to manage the process's success (like "UpdateCustomerAccount") or failure (like "SetOrderFailure").

A company or developer ought to think about implementing the Saga pattern if:

  • The program must provide data consistency amongst several microservices without tightly connecting them together.
  • Because some transactions take a long time to complete, they want to avoid the blocking of other microservices due to the prolonged operation of one microservice.
  • If an operation in the sequence fails, it must be possible to go back in time.

It is important to remember that the saga pattern becomes more complex as the number of microservices increases and that debugging is challenging. The pattern necessitates the creation of compensatory transactions for reversing and undoing modifications using a sophisticated programming methodology.

6. Circuit Breaker Pattern

Circuit Breaker is yet another fundamental design pattern in distributed systems, and it assists in overcoming the domino effect, thereby enhancing the system's reliability. It operates so that potentially failing operations are enclosed by a circuit breaker object that looks for failure. When failures exceed the specified limit, the circuit "bends,and the subsequent calls to the operation simply return an error or an option of failure without performing the task. It enables the system to fail quickly and/or protects other services that may be overwhelmed.

In Spring, you can apply the Circuit Breaker pattern with the help of Spring Cloud Circuit Breaker with Resilience4j. Here's a concise implementation:

Java
 
// Add dependency in build.gradle or pom.xml

// implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j'

 

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;

import org.springframework.stereotype.Service;

 

@Service

public class ExampleService {

 

    @CircuitBreaker(name = "exampleBreaker", fallbackMethod = "fallbackMethod")

    public String callExternalService() {

        // Simulating an external service call that might fail

        if (Math.random() < 0.7) {  // 70% chance of failure

            throw new RuntimeException("External service failed");

        }

        return "Success from external service";

    }

 

    public String fallbackMethod(Exception ex) {

        return "Fallback response: " + ex.getMessage();

    }

}

 

// In application.properties or application.yml

resilience4j.circuitbreaker.instances.exampleBreaker.failureRateThreshold=50

resilience4j.circuitbreaker.instances.exampleBreaker.waitDurationInOpenState=5000ms

resilience4j.circuitbreaker.instances.exampleBreaker.slidingWindowSize=10

 

In this instance of implementation:

  • A developer adds the @CircuitBreaker annotation to the callExternalService function.
  • When the circuit is open, the developer specifies a fallback method that will be called.
  • Configure the application configuration file's circuit breaker properties.

This configuration enhances system stability by eliminating cascade failures and allowing the service to handle errors gracefully in the external service call.

Conclusion

By applying the microservices pattern, event-driven pattern, command query responsibility segregation, API gateway pattern, saga pattern, and circuit breaker pattern with the help of Spring Boot, developers and programmers can develop distributed systems that are scalable, recoverable, easily maintainable, and subject to evolution. An extensive ecosystem of Spring Boot makes it possible to solve all the problems associated with distributed computing, which makes this framework the optimal choice for developers who want to create a cloud application. Essential examples and explanations in this article are constructed to help the reader begin using distributed system patterns while developing applications with Spring Boot. However, in order to better optimize and develop systems and make sure they can withstand the demands of today's complex and dynamic software environments, developers can investigate more patterns and sophisticated methodologies as they gain experience.

References

  1. Newman, S. (2015). Building Microservices: Designing Fine-Grained Systems. O'Reilly Media.
  2. Richards, M. (2020). Software Architecture Patterns. O'Reilly Media.
  3. AWS Documentation. (n.d.). AWS Step Functions - Saga Pattern Implementation
  4. Nygard, M. T. (2007). Release It!: Design and Deploy Production-Ready Software. Pragmatic Bookshelf.
  5. Resilience4j Documentation. (n.d.). Spring Cloud Circuit Breaker with Resilience4j. 
  6. Red Hat Developer. (2020). Microservices with the Saga Pattern in Spring Boot.
Spring Framework Spring Boot Circuit Breaker Pattern microservices Distributed Computing

Opinions expressed by DZone contributors are their own.

Related

  • Distributed Task Synchronization: Leveraging ShedLock in Spring
  • A New Era Of Spring Cloud
  • Failure Handling Mechanisms in Microservices and Their Importance
  • Chaos Engineering for Microservices

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!