# Choose a backing implementation

> For the complete documentation index, see [llms.txt](https://docs.temporal.io/llms.txt).
> Any documentation page is available as raw Markdown by appending `.md` to its URL.
> For related questions, query https://docs.temporal.io/api/retrieve?q=<question>&jtb=<job-to-be-done>&src=md-page&agent=<your-name> (no auth; returns JSON `{content, source_url, page_url}`, with `source_url` pointing to Markdown for docs pages). Don't include personal data or credentials.

> A Workflow, Standalone Activity, Update, or sync messaging can sit behind an Operation. Choose by whether the work waits, holds state, or returns a result.

The contract says nothing about what runs behind an Operation. That is the handler's private decision, and it can change later without touching callers.

Picking the wrong backing is the most common source of trouble later. This step chooses one for each Operation in the approval Service.

## Workflow

Use a Workflow for more than one step, any need to wait, or any need for durable intermediate state.

The Operation starts a Workflow and completes when that Workflow returns, so the Workflow's return value is the Operation's result. Use this when the work orchestrates several Activities, needs a timer, receives [messages](/sending-messages) while it runs, or must survive a Worker restart partway through.

A long-lived Workflow that accepts messages is still an ordinary Workflow. What makes it interactive is its message handlers and a Workflow Id you can predict.

## Standalone Activity

Use a Standalone Activity for one step with no waiting and no state: call an external API, run a computation, send a notification.

The Operation starts an Activity Execution with no parent Workflow and completes when the Activity returns. You get retries, timeouts, and a durable record of every attempt without a wrapper Workflow that exists only to call one Activity.

An Activity cannot receive messages or hold state, and cancellation only works if the Activity heartbeats. See [Nexus Standalone Activity](/nexus/standalone-activity).

## The choice for the approval Service

| Operation | Backing | Why |
| --- | --- | --- |
| `requestApproval` | Workflow | Blocks for a human decision, holds the reminder count, and accepts messages while pending |
| `remindApprover` | Sync messaging (Signal) | A Signal to the approval started by `requestApproval` |
| `submitDecision` | Update | An Update to that same approval, because the caller needs a result back |
| `attachApprovalContext` | Sync messaging (Signal-with-Start) | A Signal that also starts the approval if it does not exist yet |
| `notifyRequester` | Standalone Activity | One outbound notification, no state, nothing to wait for |

An Update is its own backing: the Operation completes when the Update completes, not when the Workflow returns. Signals are not a backing. They are [sync messaging](/nexus/temporal-operation-handler#the-nexus-aware-client), which takes effect and completes the Operation during the handler call.

The caller can observe the difference. A Signal-backed Operation completes synchronously, so a Temporal caller's Event History goes straight from `NexusOperationScheduled` to `NexusOperationCompleted`. An Update-backed Operation completes through the Nexus completion callback, which adds a `NexusOperationStarted` event carrying the Operation token used to cancel it.

The approval has to be a Workflow. It exists for a while, has an identity, and other systems interact with it during its lifetime. An Activity cannot block for a human or receive a Signal.

The notification is the opposite: a single side effect with nothing to orchestrate. A Workflow would add an Event History and a Workflow Id for no benefit, but the notification touches the outside world and can fail, so it needs the retries an Activity provides.

## Give the approval Workflow a stable Id

Derive the approval's Workflow Id from the purchase, not a random value, so that later messages can find it. A caller that knows the item id can then reach the right Execution without the handler handing out Workflow Ids.

This also makes the start idempotent: a retried Nexus start request targets the same Workflow Id rather than starting a second approval for one purchase.

Derive it in one place so the two Operations that need it cannot drift apart:

<!--SNIPSTART samples-java-nexus-walkthrough-workflow-id-->

[core/src/main/java/io/temporal/samples/nexuswalkthrough/handler/ApprovalWorkflowId.java](https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexuswalkthrough/handler/ApprovalWorkflowId.java)

```java
  public static String forItem(String itemId) {
    return "approval-" + itemId;
  }
```

<!--SNIPEND-->

In [step 8](/develop/java/nexus/development-walkthrough/send-messages), `attachApprovalContext` may start the approval before `requestApproval` is called. Both Operations derive the same Workflow Id from the same item id, so they agree on which Execution they mean.

> **💡 Tip:**
> RESOURCES
>
> - [Nexus Standalone Activity](/nexus/standalone-activity) for Activity-backed Operations.
> - [Workflow message passing](/encyclopedia/workflow-message-passing) for what makes a Workflow interactive.
> - [Nexus patterns](/nexus/patterns) for Service and Worker topology choices.
>

## Next step

- [Step 4: Implement the Service](/develop/java/nexus/development-walkthrough/implement-the-service): The approval Workflow, the handler, the development server, and a Worker.
