1- This sample shows how to create a Nexus service that is backed by a long-running workflow and
2- exposes operations that execute updates and queries against that workflow. The long-running
3- workflow, and the updates/queries, are private implementation details of the Nexus service: the
4- caller does not know how the operations are implemented.
1+ This sample shows how to expose a long-running workflow's queries, updates, and signals as Nexus
2+ operations. The caller interacts only with the Nexus service; the workflow is a private
3+ implementation detail.
54
6- This is a Java port of the
7- [ nexus_sync_operations Python sample] ( https://github.com/temporalio/samples-python/tree/main/nexus_sync_operations ) .
5+ There are ** two caller patterns** that share the same handler workflow (` GreetingWorkflow ` ):
86
9- ### Sample directory structure
7+ | | ` caller/ ` (entity pattern) | ` caller_remote/ ` (remote-start pattern) |
8+ | ---| ---| ---|
9+ | ** Who creates the workflow?** | The handler worker starts it on boot | The caller starts it via a ` runFromRemote ` Nexus operation |
10+ | ** Who knows the workflow ID?** | Only the handler | The caller chooses and passes it in every operation |
11+ | ** Nexus service** | ` NexusGreetingService ` — inputs carry only business data | ` NexusRemoteGreetingService ` — every input includes a ` workflowId ` |
12+ | ** When to use** | Single shared entity; callers don't need lifecycle control | Caller needs to create and target specific workflow instances |
1013
11- - [ ` service/GreetingService.java ` ] ( ./service/GreetingService.java ) — shared Nexus service definition with input/output types
12- - [ ` service/Language.java ` ] ( ./service/Language.java ) — shared language enum
13- - [ ` handler/ ` ] ( ./handler/ ) — Nexus operation handlers, the long-running entity workflow they back, an activity, and a handler worker
14- - [ ` caller/ ` ] ( ./caller/ ) — a caller workflow that executes Nexus operations, together with a worker and starter
14+ ### Directory structure
1515
16- ### Instructions
16+ - ` service/ ` — shared Nexus service definitions (` NexusGreetingService ` , ` NexusRemoteGreetingService ` ) and ` Language ` enum
17+ - ` handler/ ` — ` GreetingWorkflow ` and its implementation, ` GreetingActivity ` , both Nexus service impls (` NexusGreetingServiceImpl ` , ` NexusRemoteGreetingServiceImpl ` ), and the handler worker
18+ - ` caller/ ` — entity-pattern caller (workflow, worker, starter)
19+ - ` caller_remote/ ` — remote-start caller (workflow, worker, starter)
1720
18- Start a Temporal server:
21+ ### Running
22+
23+ Start a Temporal server and create namespaces/endpoint:
1924
2025``` bash
2126temporal server start-dev
22- ```
2327
24- Create the caller and handler namespaces and the Nexus endpoint:
25-
26- ``` bash
2728temporal operator namespace create --namespace nexus-sync-operations-handler-namespace
2829temporal operator namespace create --namespace nexus-sync-operations-caller-namespace
2930
@@ -33,13 +34,14 @@ temporal operator nexus endpoint create \
3334 --target-task-queue nexus-sync-operations-handler-task-queue
3435```
3536
36- In one terminal, run the handler worker (starts the long-running entity workflow and polls for
37- Nexus, workflow, and activity tasks):
37+ In one terminal, start the handler worker (shared by both patterns):
3838
3939``` bash
4040./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.handler.HandlerWorker
4141```
4242
43+ #### Entity pattern
44+
4345In a second terminal, run the caller worker:
4446
4547``` bash
@@ -52,25 +54,82 @@ In a third terminal, start the caller workflow:
5254./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller.CallerStarter
5355```
5456
55- You should see output like :
57+ Expected output:
5658
5759```
5860supported languages: [CHINESE, ENGLISH]
5961language changed: ENGLISH -> ARABIC
62+ workflow approved
63+ ```
64+
65+ #### Remote-start pattern
66+
67+ In a second terminal, run the remote caller worker:
68+
69+ ``` bash
70+ ./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller_remote.CallerRemoteWorker
71+ ```
72+
73+ In a third terminal, start the remote caller workflow:
74+
75+ ``` bash
76+ ./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_sync_operations.caller_remote.CallerRemoteStarter
77+ ```
78+
79+ Expected output:
80+
81+ ```
82+ started remote greeting workflow: nexus-sync-operations-remote-greeting-workflow
83+ supported languages: [CHINESE, ENGLISH]
84+ language changed: ENGLISH -> ARABIC
85+ workflow approved
86+ workflow result: مرحبا بالعالم
6087```
6188
6289### How it works
6390
64- The handler starts a single long-running ` GreetingWorkflow ` entity workflow when the worker boots.
65- This workflow holds the current language and a map of greetings, and exposes:
91+ #### The handler (shared by both patterns)
92+
93+ ` GreetingWorkflow ` is a long-running "entity" workflow that holds the current language and a map of
94+ greetings. It exposes its state through standard Temporal primitives:
95+
96+ - ` getLanguages ` / ` getLanguage ` — ` @QueryMethod ` s for reading state
97+ - ` setLanguage ` — an ` @UpdateMethod ` for switching between already-loaded languages
98+ - ` setLanguageUsingActivity ` — an ` @UpdateMethod ` that calls an activity to fetch a greeting for
99+ a language not yet in the map (uses ` WorkflowLock ` to serialize concurrent activity calls)
100+ - ` approve ` — a ` @SignalMethod ` that lets the workflow complete
101+
102+ The workflow waits until approved and all in-flight update handlers have finished, then returns the
103+ greeting in the current language.
104+
105+ Both Nexus service implementations translate incoming Nexus operations into calls against
106+ ` GreetingWorkflow ` stubs — queries, updates, and signals. The caller never interacts with the
107+ workflow directly.
108+
109+ #### Entity pattern (` caller/ ` + ` NexusGreetingService ` )
110+
111+ The handler worker starts a single ` GreetingWorkflow ` on boot with a fixed workflow ID.
112+ ` NexusGreetingServiceImpl ` holds that workflow ID in its constructor and routes every operation to
113+ it. The caller's inputs contain only business data (language, name), not workflow IDs.
114+
115+ ` CallerWorkflowImpl ` creates a ` NexusGreetingService ` stub and:
116+ 1 . Queries for supported languages (` getLanguages ` — backed by a ` @QueryMethod ` )
117+ 2 . Changes the language to Arabic (` setLanguage ` — backed by an ` @UpdateMethod ` that calls an activity)
118+ 3 . Confirms the change via a second query (` getLanguage ` )
119+ 4 . Approves the workflow (` approve ` — backed by a ` @SignalMethod ` )
120+
121+ #### Remote-start pattern (` caller_remote/ ` + ` NexusRemoteGreetingService ` )
122+
123+ No workflow is pre-started. Instead, ` NexusRemoteGreetingService ` adds a ` runFromRemote ` operation
124+ that starts a new ` GreetingWorkflow ` with a caller-chosen workflow ID using
125+ ` WorkflowRunOperation ` . Every other operation also includes the ` workflowId ` in its input so that
126+ ` NexusRemoteGreetingServiceImpl ` can look up the right workflow stub.
66127
67- - ` getLanguages ` — a ` @QueryMethod ` listing supported languages
68- - ` getLanguage ` — a ` @QueryMethod ` returning the current language
69- - ` setLanguage ` — an ` @UpdateMethod ` (sync) for switching between already-loaded languages
70- - ` setLanguageUsingActivity ` — an ` @UpdateMethod ` (async) that calls an activity to fetch a
71- greeting for a new language before switching
72- - ` approve ` — a ` @SignalMethod ` that allows the workflow to complete
128+ ` CallerRemoteWorkflowImpl ` creates a ` NexusRemoteGreetingService ` stub and:
129+ 1 . Starts a remote ` GreetingWorkflow ` via ` runFromRemote ` and waits for it to be running
130+ 2 . Queries, updates, and approves that workflow — same operations as the entity pattern, but each
131+ input carries the workflow ID
132+ 3 . Waits for the remote workflow to complete and returns its result (the greeting string)
73133
74- The three ` GreetingService ` Nexus operations delegate to these workflow handlers via the Temporal
75- client inside each ` OperationHandler.sync ` implementation. The caller workflow sees only the Nexus
76- operations; the entity workflow is a private implementation detail.
134+ This pattern is useful when the caller needs to control the lifecycle of individual workflow
135+ instances rather than sharing a single entity.
0 commit comments