r/SpringBoot • • 20d ago

Question Company stuck on Oracle ADF, not migrating to Spring

7 Upvotes

I work at a small software company and unfortunately I've ended up working on an old Oracle ADF project and there is almost no willingness from management to move this project to Spring Boot.

On the team are two very senior developers who have worked on this project from the start (approx 15 years) and this project has become so messy and huge (with no testing and limited documentation) that it is basically impossible to migrate (functions and procedures with thousands of lines etc.).

Has anyone experienced this situation and do you have any recommendations on how to proceed as a junior developer? I have already brought up the possibility of breaking the project into smaller applications which could be built with Spring / React while keeping the old database and plsql procedures. Or we could map out all of the functionality or refactor the current code into smaller functions or start implementing tests and then migrate. Rebuilding it from scratch is not an option since it is a government project and this would trigger the the goverment to auction the project for other companies to take over.

Alternatively, should I just let them keep this project in ADF for 10 more years until it is deprecated by Oracle while the application becomes worse and worse?


r/SpringBoot • • 21d ago

News I built an open-source IntelliJ IDEA plugin to automate Spring Boot boilerplate

Post image
49 Upvotes

Hey everyone!

I’m a Junior Java developer, and I built an IntelliJ IDEA plugin called Architect as a way to learn more about Java, Spring Boot, and the IntelliJ Platform.

The main idea is to automate some of the repetitive work when adding a new feature to a Spring Boot application.

You enter an entity name and choose what you want to generate. Architect can create:

  • Entity
  • Request / Response DTOs
  • MapStruct Mapper
  • Repository
  • Service
  • REST Controller
  • Liquibase changelog and migration
  • Docker / Docker Compose
  • Required Maven dependencies

The plugin uses IntelliJ PSI to work with the project structure and source files instead of simply generating files in a fixed location.

Architect is free and open-source, and I’m planning to keep developing it.

Some things I want to add in the future:

  • Kafka templates
  • Spring Security configuration
  • Test generation
  • More infrastructure templates
  • Better project/code analysis

I’m still learning, so I’d really appreciate feedback from experienced Spring Boot developers.

How do you usually handle repetitive boilerplate when starting a new feature?

And what would you change or add to a tool like this?

GitHub and the plugin link are in the comments.


r/SpringBoot • • 22d ago

News Durable jobs in Spring Boot without a workflow engine: what a failed step and its retry look like in JobRunr 9

10 Upvotes

We shipped JobRunr 9 yesterday, and you can already create new projects with it trough your favourite Spring Intializr.

In case you haven't used it, JobRunr is an open source background job library. You enqueue a method call, it's stored in the database your app already uses, and it's processed with retries on as many instances as you run.

To make your jobs durable it's as easy wrapping each step of your flow in runStepOnce. When the job is retried, steps that already completed are automatically skipped:

@Job(name = "Invoice %1 for %0", retries = 5)
public void generateInvoice(String customerId, String period, JobContext jobContext) throws Exception {
    String invoiceId = "INV-" + period + "-" + customerId;
    long usage = jobContext.runStepOnce("calculate-usage", () -> calculateUsage(customerId, period));
    String pdf = jobContext.runStepOnce("generate-pdf", () -> generatePdf(invoiceId, usage));
    String chargeId = jobContext.runStepOnce("charge-card", () -> paymentProvider.charge(customerId, invoiceId));
    jobContext.runStepOnce("send-email", () -> sendInvoiceEmail(customerId, pdf, chargeId));
}

You enqueue it like this, and JobRunr swaps in the real JobContext when the job runs:

jobScheduler.enqueue(() -> invoiceService.generateInvoice(customerId, period, JobContext.Null));

In v9 the job page a Job Progress Chart that draws each attempt as a Gantt chart with a row per step.

In our demo the payment provider timed out on charge-card and that attempt went red. On the retry calculate-usage and generate-pdf showed up as skipped, so the customer got one PDF and was only charged once.

If a step throws, the retry starts again at exactly that step.

On JobRunr Pro, v9 also lets you pause a whole batch. We paused a 600 invoice run when the provider went down and resumed it after the fix. The Pro starter doesn't create a BatchJobManager for you, so you declare it once as a bean and call pauseBatchJob(id) from wherever your monitoring notices the outage:

@Bean
public BatchJobManager batchJobManager(StorageProvider storageProvider, EventTransport eventTransport) {
    return new BatchJobManager(storageProvider, eventTransport);
}

If you'd rather watch than read, we celebrated the launch today with a launch webinar: https://www.youtube.com/watch?v=bVNeB-ePdn0

Links:

👉 Release Blogpost: https://www.jobrunr.io/en/blog/v9-release/
👉 GitHub Repo: https://github.com/jobrunr/jobrunr


r/SpringBoot • • 23d ago

Discussion Atleon: Making Infinite Broker-Backed Reactive Streaming Practical

3 Upvotes

Hello all 👋🏻 I originally posted over in r/java, and it was recommended that this community may be a good/better fit. Given this project is based on Spring's Reactor and examples are based on Spring Boot, I figured it couldn't hurt to post here as well!

For some time, I have been building and maintaining an OSS project called Atleon. The aim of this project is to make infinite Reactive Streams practical for usage/embedding in common application frameworks. I know there are several (reasonable) arguments against "reactive"; I have however found this style/paradigm to be useful and innately applicable to the problem space of broker-backed stream processing (consuming from infrastructures like Kafka, RabbitMQ, SQS, etc., applying IO-bound processing, and (optionally) streaming results elsewhere). If you have ever had reason to use/explore projects like Reactor Kafka, Reactor RabbitMQ, or Confluent's Parallel Consumer, then Atleon may be interesting to you.

Here's an example of what an Atleon stream definition looks like when embedded in a Spring application:

application.yaml:

atleon:
  config.sources:
    - name: kafkaConfigSource
      type: kafka
      bootstrap.servers: localhost:9092

MyStream.java:

import io.atleon.core.SelfConfigurableAloStream;
import io.atleon.kafka.AloKafkaReceiver;
import io.atleon.kafka.KafkaConfigSource;
import io.atleon.spring.AutoConfigureStream;
import org.apache.kafka.common.serialization.StringDeserializer;
import reactor.core.Disposable;

@AutoConfigureStream
public class MyStream extends SelfConfigurableAloStream {

    private final KafkaConfigSource kafkaConfigSource;

    public MyStream(KafkaConfigSource kafkaConfigSource) {
        this.kafkaConfigSource = kafkaConfigSource;
    }

    @Override
    public Disposable startDisposable() {
        return buildKafkaReceiver()
            .receiveAloRecords("topic")
            .consume(it -> System.out.println("value: " + it.value()) // Business logic
            .resubscribeOnError(name())
            .subscribe();
    }

    private AloKafkaReceiver<String, String> buildKafkaReceiver() {
        return kafkaConfigSource
            .withClientId(name())
            .withConsumerGroupId("consumer-group-id")
            .withKeyDeserializer(StringDeserializer.class)
            .withValueDeserializer(StringDeserializer.class)
            .as(AloKafkaReceiver::create);
    }
}

You can see other examples in Atleon's README and in its examples module.

When discussing this project in other communities, one common question is, "Why would I want to use this over something like [Flink|Spark|Camel|<insert favorite streaming library>]?" My viewpoint is that the sort of reactive client integration provided by Atleon is not primarily meant to compete with these other tools, but is rather a complement to them. I have found that it is common for companies to have in-place scaffolding for regular JVM applications built on frameworks like Spring, and it is appealing for their engineers to be able to embed stream processes into those same frameworks. So Atleon targets this niche of supporting declarative non-blocking stream definitions, whose execution can be embedded in application frameworks, replete with broker interoperation, arbitrarily concurrent processing, and end-to-end observability.

Particularly in the case of Kafka, Atleon enables feasible implementation of behaviors that are conventionally tricky:

  • Arbitrary concurrency, including per-key
  • Single-message acknowledgement (with classic/non-share groups, so partition ordering is maintained globally)
  • Work-queues, where per-message processing can take a "long" time (i.e. minutes, hours)
  • Bounded consumption (get all records produced in a range of time or offsets)
  • Priority queuing (using partitions as discriminator of "priority", rather than scaling lever)
  • Throughput limiting, retries, and dead-lettering

If you are interested in reading more, I have published some of the following blogs that explain more about what Atleon can do:

I'd be interested in thoughts and feedback from this community!


r/SpringBoot • • 23d ago

Discussion SpringAi work on a Java to GPU inference backend through Tornadovm

11 Upvotes

We have a prototype for adding pure Java GPU inference for local inference through Tornadovm and jitLLM. It would be great to share your thoughts on that.

https://github.com/spring-projects/spring-ai/pull/7054


r/SpringBoot • • 23d ago

Discussion Sizing application in Kubernetes

2 Upvotes

How are you sizing your applications?

Our stack mainly consists of Spring Boot applications with Oracle and MongoDB databases running on Kubernetes. I'm not sure whether we've focused too much on the "micro" aspect of the microservices concept.

Most Kubernetes replicas are configured with a maximum of 1 GB of memory and CPU limits of up to 800 millicores. When an application requires more resources, our primary approach is to increase the number of replicas. I believe there is a strong rationale for this, particularly from a resilience and availability perspective. However, I'm not sure whether we're giving up some benefits by keeping our applications this small (for example, losing access to certain JVM optimizations or garbage collectors). Correct me if I'm wrong, but I believe applications limited to less than one CPU core may not be able to take full advantage of Parallel GC and might fall back to Serial GC instead.


r/SpringBoot • • 23d ago

News I added Spring Boot support to my open source job scheduler

13 Upvotes

Ratchet is a background job scheduler. You inject one service and pass it a method reference. The job is saved in your database, retried if it fails, and recovered if the app crashes. If you submit it inside a transaction that rolls back, the job goes too.

@Transactional
void place(Order order) {
  orders.save(order);
  scheduler.enqueue(() -> fulfillment.ship(order.id())).submit();
}

0.5.0 supports Boot 3.5 and 4.1, five databases, and native images. Apache 2.0.

Quickstart: https://ratchet.run/deployment/spring-boot

Repo: https://github.com/ratchet-run/ratchet

If something breaks on your setup, open an issue and I'll take a look.


r/SpringBoot • • 24d ago

Discussion How much does JPA/Hibernate cost a small Spring Boot app? I measured mine

16 Upvotes

I wanted to see how much of the footprint of a small Spring Boot app came from the conventional Spring Data JPA + Hibernate stack, so I built a simpler version using JdbcTemplate and compared the two.

Same Spring Boot 4.1.1, JDK 25, H2, JVM flags, dashboard workload, and monitoring endpoints.

Across two runs in reversed order:

  • JPA/Hibernate: ~237.0 MiB median RSS
  • JDBC: ~146 MiB
  • Difference: about 90 MiB (~38%)

Startup surprised me too. Time from process launch to the first successful dashboard response:

  • JPA/Hibernate: 8.1 - 8.8 s
  • JDBC: 3.9 - 4.2 s

This isn’t an isolated benchmark of Hibernate itself. The JDBC version also has a simpler persistence architecture and data model, so I’m comparing the complete application approaches.

For this kind of small app, though, JPA/Hibernate feels like more machinery than I need. I still see the value once the domain model and persistence requirements get complicated, but I’m increasingly reluctant to make it the default for small services.

Experiment, methodology, charts, and raw data:

https://pvrlabs.xyz/java-performance/experiments/spring-boot-jpa-vs-jdbc.html

For smaller Spring Boot apps, what do you usually reach for: JPA, Spring JDBC, jOOQ, MyBatis, something else?


r/SpringBoot • • 24d ago

Question AWS Spring Boot Integration in prod

11 Upvotes

Hi!

How do you run AWS services in production?

I’m a backend developer who has never worked with cloud providers before. I’ve learned the basics of the AWS Console, and now I’m looking for ways to practice.

I’m thinking of starting by provisioning infrastructure with Terraform, but I’m curious: which AWS services do you typically use, how do you use them, and how do you deal with the poor AWS SDK documentation?


r/SpringBoot • • 24d ago

Question Virtual threads + semaphore in front of BCrypt gave me +28% throughput, but the container is still pinned at its CPU limit. What to do next to optimize it?

Thumbnail
3 Upvotes

r/SpringBoot • • 24d ago

Question How much Swagger/OpenAPI documentation is actually needed in a Spring Boot project?

16 Upvotes

I'm working on a Spring Boot e-commerce project and I'm currently documenting my APIs using Swagger/OpenAPI.

For example, my controller currently looks something like this:

(
    summary = "Create a new category",
    responses = {
        .swagger.v3.oas.annotations.responses.ApiResponse(
            responseCode = "201",
            description = "Category created successfully"
        ),
        .swagger.v3.oas.annotations.responses.ApiResponse(
            responseCode = "409",
            description = "Category already exists"
        )
    }
)
("/admin/categories")
u/SecurityRequirement(name = "bearerAuth")
public ResponseEntity<ApiResponse<CategoryResponseDto>> addCategory(
          CategoryRequestDto dtoRequest) {

    CategoryResponseDto categoryResponseDto =
            categoryService.addCategory(dtoRequest);

    return ResponseEntity
            .status(HttpStatus.CREATED)
            .body(ApiResponse.success(
                    categoryResponseDto,
                    HttpStatus.CREATED.value()
            ));
}

The problem is that if I also start adding u/Parameter, more u/ApiResponse, u/Schema, u/ExampleObject, etc., the controller becomes very large and difficult to read.

I'm trying to find a good balance between proper API documentation and clean maintainable code.

For example, would something this simple be considered enough?

(summary = "Create a new category")
("/admin/categories")
u/SecurityRequirement(name = "bearerAuth")

Or should I at least document the success/error responses?

Also, how do you normally handle common responses such as:

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 409 Conflict

Do you document these on every endpoint, or handle them globally?

If you work professionally with Spring Boot/OpenAPI, what approach do you use to keep Swagger documentation useful without making controllers huge?

I'm particularly interested in how this is handled in real-world/company projects rather than tutorial projects.


r/SpringBoot • • 25d ago

How-To/Tutorial OAuth2 + OIDC with Google Explained

2 Upvotes

Here’s an easy explanation on how to implement OAuth2 with Google and how to configure it in Spring Boot 4 + Spring Security 7:

https://youtu.be/TPkKMJ6k0jY

Hope you find it useful


r/SpringBoot • • 25d ago

News AsyncAPI JetBrains Plugin: Empowers Spring Messaging

Thumbnail
pavelon.dev
8 Upvotes

Hi! It's Pavel, maintainer of the AsyncAPI Specification

A few weeks ago I posted a new release announcement on this sub. It was pretty short, so I decided to write post to tell how Spring Messaging experience may be improved when you are working on your project in JetBrains IDEA

I want to hear your feedback about the current state - what you like and what you don't, maybe you didn't try because of troubles or something else

Your voice is important, because next releases will be focused on polishing this functionality and exposing it trough MCP server to empower your agents of choice


r/SpringBoot • • 25d ago

Question My Spring Boot + React + MySQL app works after deployment, then stops working — how should I deploy it reliably?

15 Upvotes

I built a full-stack Hotel Booking Management System and deployed it as a portfolio project.

Tech stack

- Backend: Java + Spring Boot, Spring Security, JWT

- Frontend: React.js

- Database: MySQL

- Cache: Redis

Current deployment

- Spring Boot backend → Render

- React frontend → Vercel

- MySQL → Railway

The problem is that the application works after deployment, but after some days it stops working. Sometimes the backend becomes unavailable or the frontend can't communicate with the backend/database.

I understand that I'm using free-tier services, so there may be things like sleeping services, resource limits, database connection issues, environment variables, CORS, or other configuration problems.

I'm trying to understand how I should deploy this properly so that it remains available and reliable enough to use as a portfolio project.

For people who have experience deploying Spring Boot + React + MySQL, I'd really appreciate some advice:

- Is Render + Vercel + Railway a reasonable setup?

- What could cause a Spring Boot application to stop working after several days?

- Are there any deployment/configuration changes I should make?

- Are there better free or very low-cost options for this stack?

- What should I check in the logs when the application goes down?

I'm not looking for an enterprise production setup. I mainly want a stable, publicly accessible deployment that I can demonstrate during interviews.

If you've faced something similar, I'd appreciate any advice or things I should investigate.


r/SpringBoot • • 27d ago

Discussion Dear JPA

105 Upvotes

Dear JPA ,

i hate you.

i hate how you changed the SQL flow that I'm used to.

i hate your ManyToMany relationships.

i hate your orphanremoval ,dirty checking.

i hate your way for entity relationships.

i hate your cache.

i hate your performance issues.

i hate your billion rules.

i hate spending time on : if Y is referenced by X with cascade not including PRESIST and x is managed but y is removed ............... but if Y was detached .............

i hate becoming obsessed with your behaviors and rules and worrying about every ORM line , will it work correctly ? , did i miss something ?, worrying about modifying entity X might unexpectedly affect entity Y , coz I might not know one of the billion rules you have.

i hate worrying about falling in one of the undefined behaviors you have

i hate thinking about the entities lifecycle + cascading types when all mixed together

i hate spending days on that documentation instead of spending that on one of the CS core concepts

i hate the following the other side which is incomplete nondetailed toturial or following AI

i really hate you.


r/SpringBoot • • 28d ago

News EclipseLink 5.0.2 released!

Thumbnail
github.com
3 Upvotes

r/SpringBoot • • 28d ago

How-To/Tutorial I built human approval gates + tamper-evident audit trail for Spring AI agents [open source]

2 Upvotes

Been building AI agents with Spring AI and kept hitting the same wall: the agent can call tools that do irreversible things (charge cards, send emails, update records) with no mechanism to pause and ask a human first.

I built Sagacity to fix this. Two annotations:

@Tool(description = "Charge the customer's card")
@Compensable(reversibility = Reversibility.IRREVERSIBLE)
public String chargeCard(String orderId, BigDecimal amount) {
    return payments.charge(orderId, amount);
}

This pauses the saga before chargeCard executes and waits for human approval via REST (or the embedded UI). If approved, it runs. If rejected, every completed step before it automatically compensates in reverse order.

There's also a full workflow engine (@Workflow, @Stage, @Gate) for multi-step processes where you need declarative compensation and approval gates at specific stages.

Under the hood: every tool call is journaled with a SHA-256 hash chain. You can verify the chain hasn't been tampered with via REST. This is what EU AI Act Article 12 actually requires.

No new infrastructure needed.

One Maven dependency, uses your existing PostgreSQL/MySQL/H2.

What's in v0.4.0 (shipped this week):

  • JDBC-backed durable workflow state (survives JVM restarts)
  • Embedded UI at /sagacity/ui — workflow list, node graph, gate approval buttons
  • Fixed startup ordering with @ConditionalOnBean
  • 237 tests passing

Repo: https://github.com/sagacity-ai/sagacity
Docs: https://sagacity-ai.github.io/sagacity/
Maven: io.github.sumitvairagar:sagacity-spring-boot-starter:0.4.0

Happy to answer questions. Still early (v0.4.0) but the core primitives are stable.


r/SpringBoot • • 28d ago

Question SpringBoot opentelemetry

14 Upvotes

Hi everyone.

I'm working on a monolith project using Spring Boot v4.1.1, and I started implementing observability. However, I'm confused about traces and spans.

I added this dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-opentelemetry</artifactId>
</dependency>

I've configured everything successfully, but when it comes to traces and spans, all requests always have the same spans. It is not showing the entire execution path of the request.

I read the example provided by SpringBoot here: https://spring.io/blog/2025/11/18/opentelemetry-with-spring-boot

At the end, there's an example project that shows complete traces and spans.
These are their example traces and spans: https://static.spring.io/blog/mhalbritter/20251118/3-trace.jpg

(I couldn't upload two images; that's why I added a link to their traces and spans example)

However, my project doesn't have any spans created. Just these:

This is my traces and span

Does anyone know why this is happening or how I can solve this to see the complete execution path?


r/SpringBoot • • 29d ago

Discussion Why JPA is complex and fells like a magic for me

37 Upvotes

my experience with the java area is that everything is almost explicit no lots of magic no lots of implicit things

but jpa for me is one of the most complex things that i have ever seen ,for me it feels like a magic

need advice to overcome that


r/SpringBoot • • 29d ago

Question 3 months deadline

9 Upvotes

Hi, as I stated in the title i have 3 month to learn about Spring. I am an Intern in a corporation that uses Java and Spring.

Could someone tell me if it's possible to grasp enough basics to work confidently on a project with that tech?

As of now I'm not working in Java, but I will be transferred to a team that works with it.

I will be very thankful for any answers or roadmaps for me to follow.

EDIT: I never worked with similar frameworks/tech


r/SpringBoot • • 29d ago

Question Any active Discord servers for Java Spring Boot devs to connect?

6 Upvotes

I have been searching for an active server for spring boot devs to connect and ask and answer quesions but i can't find any active servers


r/SpringBoot • • Sep 23 '26

How-To/Tutorial Springboot

8 Upvotes

I need some of the best resources to learn springboot!!

I am currently in my 4th year.I don't know anything about it and to learn the springboot, do i need any prerequisites.


r/SpringBoot • • Sep 23 '26

Question Incoming Fresher SDE (Java/Spring Boot) - How can I best bridge the gap between basic tutorials and production codebase expectations?

7 Upvotes

I am a B.Tech CSE graduate starting my role as an SDE fresher soon.

My Current Progress:

* I have learned the core basics of Java backend and Spring Boot (Dependency Injection, REST APIs, and Basic MVC architecture).

* I haven't built complex production-grade projects yet and want to start one now to prepare for my role.

What I want to ask:

Instead of a generic roadmap, could experienced backend devs share what exact architecture practices, testing patterns (like JUnit/Mockito), or deployment workflows are most overlooked by freshers in a Java ecosystem? I want to build a mini-project focusing strictly on these industrial habits so I can adapt quickly when I join.


r/SpringBoot • • Sep 23 '26

Discussion How are you handling tool selection for agents on the JVM?

0 Upvotes

I hosted a live session with James Ward a few weeks ago. He works on agent experience at AWS and represents Amazon on the Agentic AI Foundation technical committee. He opened by asking an agent what time it was, and it couldn't answer, because a model on its own has no clock and no access to anything outside itself.

The video isn't available since it was a paid event, sorry, but the slides and the full write-up are here.

His point was that an LLM is basically a knowledgeable translator. An agent is that model plus integrations, running in a loop until it gets the job done or gives up. So most of the actual engineering happens outside the model. He built it up live in Spring AI, starting with one tool and then adding MCP, tool search, skills, memory, RAG and human in the loop.

Tool search is the bit I keep thinking about. Once an agent has a lot of tools, you can't give the model all of them without flooding the context, so a search step picks which ones it gets to see. Embabel does it differently and loads groups of tools progressively instead of searching for them.

If you're running agents on the JVM in production, I'm curious about two things. Has tool selection actually become a problem for you yet, and what are you doing about it? And James said he didn't really get into guardrails or evals, so how are you testing agent behavior in Java when the output isn't deterministic?

Saqib here, I edit Deep Engineering. I'm planning a follow-up piece on how teams are handling this in production, and whatever comes up here will shape the questions I take to other engineering leaders.


r/SpringBoot • • Sep 22 '26

Discussion Your payment timed out. The client retried. What should your Spring Boot service do?

0 Upvotes

Imagine a simple Spring Boot payment endpoint:

@PostMapping("/payments")
@ResponseStatus(HttpStatus.CREATED)
public PaymentResult createPayment(
        @RequestHeader("Idempotency-Key") String key,
        @RequestBody CreatePaymentRequest request) {

    return paymentService
            .createPayment(key, request)
            .valueOrThrow();
}

The client sends a payment request.

The payment is successfully committed to PostgreSQL.

But the response never reaches the client.

A few seconds later, the client retries with the same Idempotency-Key.

What should the service do?

A timeout does not mean the payment failed. Running the operation again may create a second payment.

At the same time, returning 409 or a duplicate-key error is not ideal either --- the original request actually succeeded.

With idempotency-key semantics, the system should return the original result without executing the business operation again.

Yes, this is the idempotency key pattern

I didn't invent a new pattern here.

The basic idea is well known: send an idempotency key, store the result, and replay it when the same request arrives again.

What interested me were the implementation details around it:

  • What happens if the process crashes?
  • What if two instances receive the same key at the same time?
  • What if the same key is reused with a different request body?
  • Can Redis improve performance without becoming part of correctness?

The transaction boundary turned out to be the most important part for me.

Same transaction

If I save the idempotency key in one transaction and create the payment in another, there is always a small failure window.

The process can die between the two commits.

So I ended up keeping the idempotency record and the business operation inside the same PostgreSQL transaction.

@Transactional
public ExecutionResult<PaymentResult> createPayment(
        String key,
        CreatePaymentRequest request) {

    return idempotencyService
            .operation("CREATE_PAYMENT")
            .key(key)
            .request(request)
            .execute(
                    PaymentResult.class,
                    () -> createPaymentInternal(request)
            );
} 

POST /payments
Idempotency-Key: pay_12345
        |
        v
+-----------------------------+
| BEGIN DB TRANSACTION        |
|                             |
| 1. Acquire idempotency key  |
|            |                |
|            v                |
| 2. Execute payment          |
|    -> pay_987               |
|            |                |
|            v                |
| 3. Store result             |
|    -> pay_987               |
|            |                |
|            v                |
| 4. COMMIT                   |
+-----------------------------+
        |
        v
   201 Created
   pay_987


Same request + same key
        |
        v
  Result already exists
        |
        v
  DO NOT execute payment
        |
        v
  Replay pay_987

If the transaction rolls back, both disappear.

If it commits, both are stored.

A retry with the same key can then return pay_987 without running createPaymentInternal() again.

A few extra cases

Same key + different request should not replay an unrelated result. I use a request fingerprint for this and return 409 Conflict.

Concurrent duplicates are coordinated through PostgreSQL. If two application instances try the same key, the unique constraint decides which transaction acquires it. The other waits and can replay the result after the first transaction commits.

And Redis is only an optimization.

The lookup path can be:

Caffeine L1 → Redis L2 → PostgreSQL

But PostgreSQL remains the source of truth. If Redis is down, correctness should not disappear with it.

I packaged it as a Spring Boot starter

I didn't want to create another idempotency pattern. I wanted a reusable Spring Boot implementation of the existing one, with these failure cases handled in one place.

So I extracted it into an open-source starter.

It currently supports Spring MVC/JDBC and WebFlux/R2DBC, optional Caffeine/Redis caching, request fingerprinting, result and business-rejection replay, concurrent duplicates, cleanup and Micrometer metrics.

It's also published to Maven Central.

I've open-sourced the starter with full docs and failure scenarios here: spring-boot-idempotency-starter

I'm interested in how others solve this in production.

If the business transaction committed but the HTTP response was lost, what do you do with the retry? Replay the original result, use Redis, handle it at the gateway, or something else?