|
1 | 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. |
| 2 | +operations. There are two self-contained examples, each in its own directory: |
4 | 3 |
|
5 | | -There are **two caller patterns** that share the same handler workflow (`GreetingWorkflow`): |
6 | | - |
7 | | -| | `caller/` (entity pattern) | `caller_remote/` (remote-start pattern) | |
| 4 | +| | `callerpattern/` | `ondemandpattern/` | |
8 | 5 | |---|---|---| |
9 | | -| **Who creates the workflow?** | The handler worker starts it on boot | The caller starts it via a `runFromRemote` Nexus operation | |
| 6 | +| **Pattern** | Signal an existing workflow | Create and run workflows on demand | |
| 7 | +| **Who creates the workflow?** | The handler worker starts it on boot | The caller starts it via a Nexus operation | |
10 | 8 | | **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 | |
13 | | - |
14 | | -### Directory structure |
15 | | - |
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) |
20 | | - |
21 | | -### Running |
22 | | - |
23 | | -Start a Temporal server: |
24 | | - |
25 | | -```bash |
26 | | -temporal server start-dev |
27 | | -``` |
28 | | -Create the namespaces and Nexus endpoint: |
29 | | - |
30 | | -```bash |
31 | | -temporal operator namespace create --namespace nexus-messaging-handler-namespace |
32 | | -temporal operator namespace create --namespace nexus-messaging-caller-namespace |
33 | | - |
34 | | -temporal operator nexus endpoint create \ |
35 | | - --name nexus-messaging-nexus-endpoint \ |
36 | | - --target-namespace nexus-messaging-handler-namespace \ |
37 | | - --target-task-queue nexus-messaging-handler-task-queue |
38 | | -``` |
39 | | - |
40 | | -In one terminal, start the handler worker (shared by both patterns): |
41 | | - |
42 | | -```bash |
43 | | -./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_messaging.handler.HandlerWorker --args="-target-host localhost:7233 -namespace my-target-namespace" |
44 | | -``` |
45 | | - |
46 | | -#### Entity pattern |
47 | | - |
48 | | -In the second terminal, run the caller worker: |
49 | | - |
50 | | -```bash |
51 | | -./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_messaging.caller.CallerWorker --args="-target-host localhost:7233 -namespace my-caller-namespace" |
52 | | -``` |
53 | | - |
54 | | -In the third terminal, start the caller workflow: |
55 | | - |
56 | | -```bash |
57 | | -./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_messaging.caller.CallerStarter --args="-target-host localhost:7233 -namespace my-caller-namespace" |
58 | | -``` |
59 | | - |
60 | | -Expected output: |
61 | | - |
62 | | -``` |
63 | | -supported languages: [CHINESE, ENGLISH] |
64 | | -language changed: ENGLISH -> ARABIC |
65 | | -workflow approved |
66 | | -``` |
67 | | - |
68 | | -#### Remote-start pattern |
69 | | - |
70 | | -In a second terminal, run the remote caller worker: |
71 | | - |
72 | | -```bash |
73 | | -./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_messaging.caller_remote.CallerRemoteWorker --args="-target-host localhost:7233 -namespace my-caller-namespace" |
74 | | -``` |
75 | | - |
76 | | -In a third terminal, start the remote caller workflow: |
77 | | - |
78 | | -```bash |
79 | | -./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexus_messaging.caller_remote.CallerRemoteStarter --args="-target-host localhost:7233 -namespace my-caller-namespace" |
80 | | -``` |
81 | | - |
82 | | -Expected output: |
83 | | - |
84 | | -``` |
85 | | -started remote greeting workflow: nexus-messaging-remote-greeting-workflow |
86 | | -supported languages: [CHINESE, ENGLISH] |
87 | | -language changed: ENGLISH -> ARABIC |
88 | | -workflow approved |
89 | | -workflow result: مرحبا بالعالم |
90 | | -``` |
91 | | - |
92 | | -### How it works |
93 | | - |
94 | | -#### The handler (shared by both patterns) |
95 | | - |
96 | | -`GreetingWorkflow` is a long-running "entity" workflow that holds the current language and a map of |
97 | | -greetings. It exposes its state through standard Temporal primitives: |
98 | | - |
99 | | -- `getLanguages` / `getLanguage` — `@QueryMethod`s for reading state |
100 | | -- `setLanguage` — an `@UpdateMethod` for switching between already-loaded languages |
101 | | -- `setLanguageUsingActivity` — an `@UpdateMethod` that calls an activity to fetch a greeting for |
102 | | - a language not yet in the map (uses `WorkflowLock` to serialize concurrent activity calls) |
103 | | -- `approve` — a `@SignalMethod` that lets the workflow complete |
104 | | - |
105 | | -The workflow waits until approved and all in-flight update handlers have finished, then returns the |
106 | | -greeting in the current language. |
107 | | - |
108 | | -Both Nexus service implementations translate incoming Nexus operations into calls against |
109 | | -`GreetingWorkflow` stubs — queries, updates, and signals. The caller never interacts with the |
110 | | -workflow directly. |
111 | | - |
112 | | -#### Entity pattern (`caller/` + `NexusGreetingService`) |
113 | | - |
114 | | -The handler worker starts a single `GreetingWorkflow` on boot with a fixed workflow ID. |
115 | | -`NexusGreetingServiceImpl` holds that workflow ID in its constructor and routes every operation to |
116 | | -it. The caller's inputs contain only business data (language, name), not workflow IDs. |
117 | | - |
118 | | -`CallerWorkflowImpl` creates a `NexusGreetingService` stub and: |
119 | | -1. Queries for supported languages (`getLanguages` — backed by a `@QueryMethod`) |
120 | | -2. Changes the language to Arabic (`setLanguage` — backed by an `@UpdateMethod` that calls an activity) |
121 | | -3. Confirms the change via a second query (`getLanguage`) |
122 | | -4. Approves the workflow (`approve` — backed by a `@SignalMethod`) |
123 | | - |
124 | | -#### Remote-start pattern (`caller_remote/` + `NexusRemoteGreetingService`) |
125 | | - |
126 | | -No workflow is pre-started. Instead, `NexusRemoteGreetingService` adds a `runFromRemote` operation |
127 | | -that starts a new `GreetingWorkflow` with a caller-chosen workflow ID using |
128 | | -`WorkflowRunOperation`. Every other operation also includes the `workflowId` in its input so that |
129 | | -`NexusRemoteGreetingServiceImpl` can look up the right workflow stub. |
| 9 | +| **Nexus service** | `NexusGreetingService` | `NexusRemoteGreetingService` | |
130 | 10 |
|
131 | | -`CallerRemoteWorkflowImpl` creates a `NexusRemoteGreetingService` stub and: |
132 | | -1. Starts a remote `GreetingWorkflow` via `runFromRemote` and waits for it to be running |
133 | | -2. Queries, updates, and approves that workflow — same operations as the entity pattern, but each |
134 | | - input carries the workflow ID |
135 | | -3. Waits for the remote workflow to complete and returns its result (the greeting string) |
| 11 | +Each directory is fully self-contained with its own `service/`, `handler/`, and caller code. The |
| 12 | +`GreetingWorkflow` and `GreetingWorkflowImpl` classes are **identical** between the two — only the |
| 13 | +Nexus service interface and its implementation differ. This highlights that the same workflow can be |
| 14 | +exposed through Nexus in different ways depending on whether the caller needs lifecycle control. |
136 | 15 |
|
137 | | -This pattern is useful when the caller needs to control the lifecycle of individual workflow |
138 | | -instances rather than sharing a single entity. |
| 16 | +See each directory's README for running instructions. |
0 commit comments