Do you want your ad here?

Contact us to get your ad seen by thousands of users every day!

[email protected]

Toward a Durable Spring PetClinic

  • July 19, 2026
  • 6 min read
Likes ...
Comments ...
Table of Contents
OverviewWhat makes a process a good candidate for durability?Spring PetClinic as an Exploratory DeviceStep 1 — Download the Spring PetClinic sourceStep 2 — Analyze the application for durability candidatesStep 3 — Conclusions

Overview

Durable Execution is a way of running code so that its progress survives failure. A normal program keeps its state in memory: if the process crashes, the machine reboots, or a network call times out halfway through a multi-step operation, that state is gone and the work is left half-done.

Temporal, the Durable Execution platform, persists every step of a process to its Event History — a durable, append-only log of Events — so that after any failure the work resumes exactly where it left off, as if the crash never happened.

That makes it possible to write long-running, multi-step processes (ones that wait days for a Timer, call flaky external services, or must never run a step twice) as ordinary, linear code, while the platform guarantees they complete reliably and exactly once.

What makes a process a good candidate for durability?

But what makes a process a good candidate for durability? The processes that benefit most are the ones that are:

  • long-running (they wait minutes, days, or weeks)

  • multi-step (they touch several systems that can each fail independently),

  • and/or that must run exactly once (never double-charging a card or double-sending a notification).

A plain, single-step database write that either commits or doesn't gets nothing from Durable Execution — it is already atomic.

The interesting question is where a real application hides processes of the first three kinds.

Spring PetClinic as an Exploratory Device

To answer it concretely, let's take the canonical Spring PetClinic sample application and interrogate its source code.

Spring PetClinic is a textbook CRUD app, so nothing in it strictly requires durability today — but its domain (booking visits, registering pets, taking payments) is full of exactly those long-running, multi-step, failure-prone processes, whether latent in the code or one obvious extension away. The goal is to find those seams, rank them, and pick a starting point.

The sections that follow work through it in order: Step 1 downloads the source to analyze, Step 2 runs the durability analysis twice (once with plain, standard Claude Code, then again grounded in Temporal), and Step 3 draws the conclusions.

The result is a single and very clear recommendation in the context of the Spring PetClinic: make visit booking + reminder durable first. It wins for three reasons.

  1. The seam already exists in the code — VisitController persists a future-dated visit today, so there is a natural place to hook in with almost no scaffolding.

  2. A visit is inherently long-running: the reminder must fire the day before an appointment that may be weeks away, which is exactly the kind of durable wait that in-memory code cannot survive but Durable Execution handles trivially.

  3. It exercises all three things durability is for in a single, easy-to-grasp process — a durable Timer (sleep until the reminder is
    due), retriable side effects (send the confirmation, notify the vet), and exactly-once semantics (never double-send a reminder, even across a crash).

The other candidates are real, but they are either lower-stakes (owner onboarding), require inventing new capabilities (billing / payment), or add distributed-system complexity (external registries) before the core value is demonstrated. Visit booking is the shortest path to seeing durability earn its keep.

Step 1 — Download the Spring PetClinic source

The first step of this project is to download the official Spring PetClinic sample application.

Step 2 — Analyze the application for durability candidates

Pointing Claude Code (or a similar AI) at the source of an application you already have and asking it where a technology you're interested in — adopting, integrating, or simply learning about — would fit is a natural first step.

The model reads your real code rather than a whitepaper, so you never have to guess how a new technology maps onto what you already have. The opportunities it surfaces are grounded in your actual domain and control flow, not in generic advice.

That is exactly what this step does: it analyzes Spring PetClinic for processes worth making durable, and it does it twice.

  • 2a is a pass done with plain, standard Claude Code — no plugins or skills installed. It identifies the candidates, ranks them, and picks visit booking + reminder as the ideal first process.

  • 2b re-runs the same analysis grounded in Temporal (via the temporal-developer skill), mapping each candidate onto concrete Temporal primitives and adding temporal-spring-boot-starter wiring.

Both reach the same conclusions; 2b adds Temporal-specific vocabulary, corrected line numbers, and implementation guidance. See Section 3 for a side-by-side comparison.

2a — Analysis: processes that are good candidates for durability

Spring PetClinic is a textbook CRUD app. Every "process" is a single synchronous HTTP request that performs one local database transaction against one datasource. As it stands there are no external service calls, no message queues, no multi-step orchestration, no background jobs, and no long-running operations in the code. So out of the box nothing strictly requires durable execution — a single JPA transaction already provides atomicity.

The useful question is therefore not "what is durable today" but "where does the domain naturally want a multi-step, failure-prone, or long-running process that durability would protect?" Some of those seams already exist in the code;
others are obvious extensions the domain implies.

Candidates grounded in the current code

  1. Book a visitowner/VisitController.java:97 (processNewVisitForm). Today this is just owners.save(owner). But "booking a visit" is the app's most obvious real-world workflow: in production it would fan out to send a confirmation, schedule a reminder for the day before the appointment, and notify the assigned vet. That reminder is a durable timer — the process sleeps until visit.getDate() minus one day, then acts. This is the strongest candidate: it is long-running (days to weeks), spans side effects that can fail independently, and needs exactly-once semantics on notifications.

  2. Create / update a petowner/PetController.java:107 (processCreationForm) and owner/PetController.java:186 (updatePetDetails). Currently a single saveAndFlush. A realistic clinic extension registers the pet with external systems (microchip registry, insurance provider, vaccination-record service). The moment a second system is involved, this becomes a distributed multi-step process with partial-failure risk — a good fit for per-step retries and a saga / compensation path.

  3. Register an ownerowner/OwnerController.java:77 (processCreationForm). The same pattern at lower priority: a welcome message plus a CRM sync turns a local save into a process worth making durable.

Candidates from natural domain extensions (not in code yet)

  1. Vet reference-data sync / cache warmingsystem/CacheConfiguration.java with vet/VetController.java. The vets query is explicitly cached, which hints at expensive or refreshable data. A scheduled job to warm/refresh the cache, or to sync vet and specialty data from an external HR system, is a periodic durable process.

  2. Visit billing / payment (new capability) — the domain's biggest missing durability candidate. Payment capture → invoice generation → receipt delivery is the canonical saga: an external payment gateway call (retryable, must be exactly-once) followed by dependent steps that need compensation if a later step fails.

Priority ranking

RankProcessWhy durableEffort to demo
1Visit booking + reminderLong-running timer, multiple side effects, exactly-once notificationsLow — hook exists at VisitController.java:97
2Pet registration → external registriesDistributed multi-step, partial failure, sagaMedium
3Visit billing / payment (new)Payment saga, compensationMedium
4Owner onboardingWelcome message + CRM syncLow
5Vet / reference-data syncScheduled periodic jobLow

Recommendation

Given our intent and that VisitController already has a clean insertion point, visit booking with a reminder is the ideal first process to make durable — it exercises the three things durability is for (durable timers, retriable side effects, and exactly-once execution) with minimal scaffolding.

2b — Analysis grounded in Temporal

This section re-runs the same analysis, but grounded in Temporal as the Durable Execution platform, using the temporal-developer skill for terminology and Java/Spring Boot patterns. The candidates and ranking are unchanged from 2a; what follows maps each one onto concrete Temporal primitives.

Why nothing needs Temporal today

Every state change is a single synchronous HTTP handler wrapping one JPA transaction against one datasource:

HandlerPersist callLocation
OwnerController.processCreationFormowners.save(owner)OwnerController.java:84
OwnerController.processUpdateOwnerFormowners.save(owner)OwnerController.java:156
PetController.processCreationFormowners.saveAndFlush(owner)PetController.java:126
PetController.updatePetDetailsowners.saveAndFlush(owner)PetController.java:199
VisitController.processNewVisitFormowners.save(owner)VisitController.java:109

There are no external calls, queues, timers, background jobs, or multi-step orchestration. Each commit is already atomic, so in Temporal terms nothing here needs durable execution yet. Two signals in the code show where the domain wants it:

  • Future-dated work already exists. VisitController.minVisitDate() (VisitController.java:83) forces every visit ≥1 day out, and Visit carries a date — exactly what a Temporal Timer (Workflow.sleep(...)) serves.
  • An explicit cache implies refreshable data. CacheConfiguration.java:37 creates a dedicated vets cache served by VetController.java:70 — a hint at data worth periodically syncing via a Temporal Schedule.

Candidates mapped to Temporal primitives

RankProcessCode seamTemporal fit
1Visit booking + reminderVisitController.java:109Workflow with Workflow.sleep() until visit.getDate() − 1 day, then Activities for confirmation + vet notification. Exactly-once via Event History replay.
2Pet registration → external registriesPetController.java:126, :199Saga — microchip / insurance / vaccination each an Activity with per-step retries and idempotent compensations.
3Visit billing / payment (new)Canonical Saga: charge (retryable, exactly-once) → invoice → receipt, compensating on later failure.
4Owner onboardingOwnerController.java:84Short Workflow: welcome message + CRM sync as retriable Activities.
5Vet / reference-data syncCacheConfiguration.java:37 + VetController.java:70Periodic Temporal Schedule driving an Activity that refreshes the vets cache.

Recommendation

Start with visit booking + reminder at VisitController.java:109 (right after validation). In Temporal terms it exercises the three things Durable Execution is for in one Workflow:

  • TimerWorkflow.sleep() until the day before the appointment, surviving Worker restarts.
  • Retriable Activities — confirmation email + vet notification, each with a Retry Policy.
  • Exactly-once semantics — Event History replay guarantees the reminder never double-sends across crashes.

Wiring uses temporal-spring-boot-starter: keep @WorkflowInterface / @ActivityInterface on interfaces, add @WorkflowImpl / @ActivityImpl on implementations, and inject the auto-configured WorkflowClient bean into VisitController to start the booking workflow after the save.

Step 3 — Conclusions

Sections 2a and 2b reach the same conclusions — identical candidates, identical ranking, and the same #1 pick (visit booking + reminder). They differ only in framing and sourcing:

Aspect2a2b
ToolingPlain, standard Claude Code — no plugins or skills; treats "durability" as an abstract conceptGrounded in Temporal, using the installed temporal-developer skill
VocabularyGeneric: "durable timer", "saga / compensation path", "scheduled job", "exactly-once"Concrete Temporal primitives: Workflow, Activities with Retry Policies, Saga, Temporal Schedule, Timer, Event History replay
Line numbersCites upstream PetClinic lines (e.g. VisitController.java:97, PetController.java:107, OwnerController.java:77)Cites the actual checked-out source lines (e.g. VisitController.java:109, PetController.java:126, OwnerController.java:84)
"Why nothing needs it today"Prose paragraphSame point, but as a table listing each handler and its exact persist call
Implementation guidanceNone — stays at the analysis levelAdds concrete temporal-spring-boot-starter wiring

Two takeaways:

  1. 2b's line numbers are the accurate ones. 2a's numbers predate the current checkout and drift from the source on disk; 2b corrects them against the code actually read.
  2. The analysis is deliberately run twice — 2a frames the question with plain Claude Code, 2b answers it with Temporal specifics via the temporal-developer skill. The recommendation is stable across both: make visit booking + reminder durable first.
  • July 19, 2026
  • 6 min read
Likes ...
Comments ...

Do you want your ad here?

Contact us to get your ad seen by thousands of users every day!

[email protected]

Comments (0)

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

No comments yet. Be the first.

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard