Install
$ agentstack add skill-resonatehq-resonate-skills-resonate-external-system-of-record-pattern-java ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Resonate External System of Record Pattern — Java
> Prerelease note. resonate-sdk-java is published on Maven Central — pin io.resonatehq:resonate-sdk-java:0.1.1. The API mirrors the Python SDK and may change before a stable 1.0. Requires Java 21+ (virtual threads, a feature generally available in Java 21). There is no Java system-of-record example repo yet, so every code block here uses only documented SDK surface, compile-verified against 0.1.1 and cross-checked against develop/java.mdx (docs PR #230) and the SDK source.
Overview
For the language-agnostic framing and the "Write Last, Read First" mental model, see resonate-external-system-of-record-pattern-typescript. This skill translates that model to idiomatic Java: ctx.run-based checkpointing, type-keyed dependency injection for the external client, and idempotency keys derived from the durable execution's stable ID.
The core contract is simple: one system owns the truth. Resonate coordinates the workflow and guarantees at-least-once execution; the external system enforces consistency through its own primitives — idempotency keys, upserts, conditional writes, ledger deduplication.
When to use
- A Resonate workflow must create, update, or read from a database, payment processor, ledger, or any external store that has its own durability.
- You need multi-step operations (reserve → charge → record) to be safe to retry without double-writes.
- You cannot use distributed transactions across Resonate's promise store and the external system.
Do not use this pattern when all writes live inside a single ACID transaction scope — use the transaction directly.
The replay principle — wrap every SoR call in ctx.run
Whenever a workflow suspends and resumes (after a ctx.sleep, a remote ctx.rpc, or a pending ctx.promise), the entire workflow body re-executes from the top. Durable child promises short-circuit steps that already settled — the external call is not re-fired, and the stored result is returned directly. But any external call NOT wrapped in a ctx.run will execute again on every replay, producing double-writes.
Rule: every read from or write to the external SoR must be inside its own ctx.run leaf.
// BAD — fires on every replay, may charge the card twice
public static String placeOrder(Context ctx, OrderArgs args) {
String chargeId = stripe.charge(args.amountCents(), args.cardToken()); // replay fires this again
return chargeId;
}
// GOOD — checkpointed; on replay the stored chargeId is returned, Stripe is not called again
public static String placeOrder(Context ctx, OrderArgs args) {
return ctx.run(Orders::chargeCard, args).await();
}
Reaching the SoR client via dependency injection
The Java SDK uses type-keyed DI. Register the client once on the instance with r.withDependency, and fetch it inside a leaf by type with ctx.getDependency. (Go takes a different path — it closes the client over the leaf function.)
import io.resonatehq.resonate.Context;
// In the ephemeral world, before processing starts:
// r.withDependency(new PaymentGateway(System.getenv("STRIPE_KEY")));
public static String chargeCard(Context ctx, ChargeArgs args) {
PaymentGateway gateway = ctx.getDependency(PaymentGateway.class);
// Idempotency key derived from a STABLE value — never time/random.
String idempotencyKey = "charge:" + args.orderId();
return gateway.submit(args.amountCents(), args.cardToken(), idempotencyKey);
}
Dependencies are keyed by their concrete class — register at most one instance per type. This keeps the SoR client out of the serialized args while still being reachable from any worker that runs the leaf.
Idempotency keys from ctx.info().id()
A ctx.run step can re-execute on retry before it settles (Resonate delivers at-least-once). The SoR must be addressed idempotently so a duplicate call is harmless. Derive stable keys from ctx.info().id(), ctx.info().originId(), or stable workflow args — never from System.currentTimeMillis() or Math.random(), which differ across replays and defeat deduplication.
public static long applyBalanceDelta(Context ctx, AccountArgs args) {
Ledger ledger = ctx.getDependency(Ledger.class);
String idempotencyKey = args.accountId() + ":" + ctx.info().id() + ":adjust";
return ledger.apply(args.accountId(), args.delta(), idempotencyKey);
}
ctx.info().id() is stable across replays, so the derived key is identical on every retry — the SoR deduplicates.
Full order workflow: reserve → charge → record
Three sequential SoR interactions, each in its own ctx.run. Any step that already settled short-circuits on replay; no external system is hit twice.
import io.resonatehq.resonate.Context;
public final class Orders {
private Orders() {}
public record OrderArgs(String orderId, String sku, int quantity, long amountCents, String cardToken) {}
public record ReserveArgs(String orderId, String sku, int quantity) {}
public record ChargeArgs(String orderId, long amountCents, String cardToken) {}
public record RecordArgs(String orderId, String chargeId) {}
/** Orchestrates three idempotent SoR steps. Each ctx.run is checkpointed. */
public static String createOrder(Context ctx, OrderArgs args) {
// Step 1 — reserve inventory; idempotent via ON CONFLICT in the DB
String reservationId = ctx.run(Orders::reserveInventory,
new ReserveArgs(args.orderId(), args.sku(), args.quantity())).await();
// Step 2 — charge payment; idempotent via the gateway's idempotency key
String chargeId = ctx.run(Orders::chargeCard,
new ChargeArgs(args.orderId(), args.amountCents(), args.cardToken())).await();
// Step 3 — record the order; idempotent via ON CONFLICT (order_id) DO NOTHING
ctx.run(Orders::recordOrder, new RecordArgs(args.orderId(), chargeId)).await();
return args.orderId();
}
public static String reserveInventory(Context ctx, ReserveArgs args) {
Inventory inv = ctx.getDependency(Inventory.class);
return inv.reserve(args.orderId(), args.sku(), args.quantity()); // ON CONFLICT → existing id
}
public static String chargeCard(Context ctx, ChargeArgs args) {
PaymentGateway gateway = ctx.getDependency(PaymentGateway.class);
return gateway.submit(args.amountCents(), args.cardToken(), "charge:" + args.orderId());
}
public static String recordOrder(Context ctx, RecordArgs args) {
OrderStore store = ctx.getDependency(OrderStore.class);
store.insertIfAbsent(args.orderId(), args.chargeId()); // ON CONFLICT (order_id) DO NOTHING
return "recorded";
}
}
Check-then-act made replay-safe
A common SoR pattern is: read current state, branch on it, then apply a mutation. Both the read and the write must be in separate ctx.run calls. Reading the SoR in bare workflow code is unsafe — on replay the bare read fires again and may observe a different value.
public static long adjustBalance(Context ctx, AccountArgs args) {
// Checkpointed read — on replay returns the stored snapshot, not a fresh DB hit.
AccountState state = ctx.run(Orders::getAccountState, args.accountId()).await();
// Branch in plain workflow code — no external calls here.
if (state.status().equals("frozen")) {
throw new IllegalStateException("account " + args.accountId() + " is frozen");
}
if (state.balance() + args.delta() `) from by-name results, retry exhaustion
- `durable-execution` — foundational replay semantics; this pattern is checkpoint-centric
- `resonate-external-system-of-record-pattern-typescript` — language-agnostic mental model
- `resonate-external-system-of-record-pattern-python` — the closest sibling; the Java DI API mirrors Python
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [resonatehq](https://github.com/resonatehq)
- **Source:** [resonatehq/resonate-skills](https://github.com/resonatehq/resonate-skills)
- **License:** Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.