Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
Software Architecture with Spring

You're reading from   Software Architecture with Spring Design scalable and high-performance Java applications with Spring

Arrow left icon
Product type Paperback
Published in Jun 2025
Publisher Packt
ISBN-13 9781835880609
Length 464 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Wanderson Xesquevixos Wanderson Xesquevixos
Author Profile Icon Wanderson Xesquevixos
Wanderson Xesquevixos
Arrow right icon
View More author details
Toc

Table of Contents (21) Chapters Close

Preface 1. Part 1:Foundations of Software Architecture
2. Chapter 1: Diving into Software Architecture FREE CHAPTER 3. Chapter 2: Decision-Making Processes in Software Architecture 4. Chapter 3: Understanding the System Context 5. Part 2: Exploring Architectural Styles
6. Chapter 4: Monolithic Architecture 7. Chapter 5: Client-Server Architecture 8. Chapter 6: Microservices Architecture 9. Chapter 7: Microservices Patterns with Spring Cloud 10. Chapter 8: Event-Driven Architecture 11. Chapter 9: Pipe-and-Filter and Serverless Architecture 12. Part 3: Advanced Topics in Modern Software Development
13. Chapter 10: Security 14. Chapter 11: Observability 15. Chapter 12: Testing 16. Chapter 13: Performance and Optimizations 17. Chapter 14: Orchestration with Kubernetes 18. Chapter 15: Continuous Integration and Continuous Deployment 19. Index 20. Other Books You May Enjoy

Exploring event-driven architecture patterns

Event-driven architecture leverages various design patterns to effectively handle events and ensure robust, scalable, and maintainable systems. These patterns help manage event generation, transmission, processing, and storage complexities. We will present some commonly used patterns in event-driven architecture.

Publish-subscribe

The publish-subscribe pattern in event-driven architecture involves producers sending events to a broker, which distributes the events to multiple consumers. Messages are pushed to consumers, and consumers can be added or removed without affecting the producer. A durable subscription allows inactive consumers to receive missed events upon reconnection.

An example is a social media platform where users subscribe to updates from other users. When a user posts a new update, it is published to all subscribers.

Event notification

Event notification is a simple pattern where the producer emits an event to inform consumers that a change or action has occurred. These events carry minimal information, so consumers may need to fetch additional data if required. While this approach reduces event size and is easy to implement, it can increase latency and create tighter coupling by requiring follow-up requests.

The following JSON snippet illustrates an example of a user placing a bid on an item in the auction system:

{
    "userId": "1",
    "productId": "2",
    "bidAmount": 150.00
}
WITH:
{
    "eventType": "BidAdded",
    "userId": "1",
    "productId": "2",
    "bidAmount": 150.00
}

Note that the event notification pattern only includes the user and product IDs, so the event contains minimal information for other services. Figure 8.6 illustrates service interactions using an event notification pattern.

Figure 8.6: Service interaction using the event notification pattern

Figure 8.6: Service interaction using the event notification pattern

If the consumer needs more user or product data, additional service requests are required, creating a coupling between services. This can cause delays, lower system performance, and create dependencies that make the system harder to manage and maintain. Possible solutions to minimize the tight coupling between services would be implementing a shared cache or using the pattern event-carried state transfer.

Event-carried state transfer

In the event-carried state transfer pattern, events contain all the information consumers need to process, eliminating the need to fetch additional data.

The following code snippet presents an example of the previous JSON with complete user and item data:

{
  "user": {
    "id": "112233",
    "name": "Wanderson Xesquevixos"
  },
  "product": {
    "id": "98765",
    "description": "Sport Car 1977"
  },
  "bidAmount": 150.00
}

This reduces latency by providing all necessary data within the event and simplifies consumer logic. However, events can become large and may contain redundant information.

Message outbox pattern

The message outbox pattern ensures reliable event publishing in distributed systems while preserving data consistency. Before sending a message, the service saves it to a database outbox table within the same transaction that updates the business data. A background process then reads from this table and publishes the event to the broker. This approach prevents message loss if the service crashes after committing the transaction but before sending the event, ensuring that every persisted change is eventually reflected in the event stream. An example is an e-commerce application that saves an order in the database while simultaneously storing an OrderPlaced event in the outbox table. This event is later published to notify other services, maintaining a consistent view between the internal data state and external event consumers.

Message inbox pattern

The message inbox pattern consumes and processes messages reliably in distributed systems while preserving data consistency. When a service receives an event, it first stores the message in a dedicated inbox table before executing any business logic. This approach helps prevent duplicate processing and enables exactly-once or idempotent execution, even in crashes or retries. By persisting the message before processing, the system ensures that operations are only applied once, maintaining consistency between the event stream and the service’s internal state. For example, a billing service receiving a PaymentConfirmed event writes the message to its inbox table and checks whether it has already been processed. Only then does it apply the payment logic, avoiding double charging and ensuring a consistent transactional outcome.

Saga pattern

The Saga pattern manages long-running transactions and ensures data consistency across multiple services by breaking a transaction into smaller steps, each of which can be compensated if it fails. It represents a single business process, as illustrated in Figure 8.7.

Figure 8.7: The Saga pattern process

Figure 8.7: The Saga pattern process

In a travel booking system, a saga coordinates the booking of flights, hotels, and vehicle rentals. If any step fails, the saga triggers compensating actions to cancel the already completed bookings.

The Saga pattern ensures data consistency and handles long-running transactions. However, it is complex to implement and manage compensating transactions.

We’ve learned many new concepts; now, let’s move on to the next section and apply them by implementing event-driven services in our online auction application.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Visually different images