@temporal-architect/claude-plugin 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +38 -0
- package/package.json +37 -0
- package/skills/MANIFEST.json +373 -0
- package/skills/MANIFEST.md +121 -0
- package/skills/temporal-architect/SKILL.md +99 -0
- package/skills/temporal-architect/reference/decomposition.md +78 -0
- package/skills/temporal-architect-author-go/README.md +16 -0
- package/skills/temporal-architect-author-go/SKILL.md +191 -0
- package/skills/temporal-architect-author-go/SUBAGENT_ADOPTION.md +161 -0
- package/skills/temporal-architect-author-go/reference/activity-call.md +73 -0
- package/skills/temporal-architect-author-go/reference/activity-def.md +54 -0
- package/skills/temporal-architect-author-go/reference/assignment.md +36 -0
- package/skills/temporal-architect-author-go/reference/await-all.md +104 -0
- package/skills/temporal-architect-author-go/reference/await-one.md +193 -0
- package/skills/temporal-architect-author-go/reference/await-timer.md +35 -0
- package/skills/temporal-architect-author-go/reference/close.md +71 -0
- package/skills/temporal-architect-author-go/reference/composite-patterns.md +176 -0
- package/skills/temporal-architect-author-go/reference/condition.md +56 -0
- package/skills/temporal-architect-author-go/reference/control-flow.md +151 -0
- package/skills/temporal-architect-author-go/reference/dependency-resolution.md +29 -0
- package/skills/temporal-architect-author-go/reference/detach.md +52 -0
- package/skills/temporal-architect-author-go/reference/heartbeat.md +84 -0
- package/skills/temporal-architect-author-go/reference/nexus-service-def.md +73 -0
- package/skills/temporal-architect-author-go/reference/nexus.md +35 -0
- package/skills/temporal-architect-author-go/reference/options.md +138 -0
- package/skills/temporal-architect-author-go/reference/promise.md +73 -0
- package/skills/temporal-architect-author-go/reference/proto-driven.md +197 -0
- package/skills/temporal-architect-author-go/reference/query-handler.md +34 -0
- package/skills/temporal-architect-author-go/reference/signal-handler.md +73 -0
- package/skills/temporal-architect-author-go/reference/three-layer-testing.md +173 -0
- package/skills/temporal-architect-author-go/reference/types.md +72 -0
- package/skills/temporal-architect-author-go/reference/update-handler.md +64 -0
- package/skills/temporal-architect-author-go/reference/worker.md +215 -0
- package/skills/temporal-architect-author-go/reference/workflow-call.md +37 -0
- package/skills/temporal-architect-author-go/reference/workflow-def.md +45 -0
- package/skills/temporal-architect-author-infra/README.md +16 -0
- package/skills/temporal-architect-author-infra/SKILL.md +132 -0
- package/skills/temporal-architect-author-infra/reference/tcld.md +112 -0
- package/skills/temporal-architect-author-infra/reference/terraform.md +125 -0
- package/skills/temporal-architect-design/README.md +16 -0
- package/skills/temporal-architect-design/SKILL.md +224 -0
- package/skills/temporal-architect-design/reference/LANGUAGE.md +5 -0
- package/skills/temporal-architect-design/reference/anti-patterns.md +332 -0
- package/skills/temporal-architect-design/reference/common-errors.md +88 -0
- package/skills/temporal-architect-design/reference/core-principles.md +52 -0
- package/skills/temporal-architect-design/reference/design-checklist.md +59 -0
- package/skills/temporal-architect-design/reference/namespaces.md +84 -0
- package/skills/temporal-architect-design/reference/notation-examples.md +304 -0
- package/skills/temporal-architect-design/reference/notation-reference.md +70 -0
- package/skills/temporal-architect-design/reference/primitives-reference.md +65 -0
- package/skills/temporal-architect-design/reference/project-discovery-subagent.md +80 -0
- package/skills/temporal-architect-design/reference/reverse-engineering.md +53 -0
- package/skills/temporal-architect-design/reference/twf-conventions.md +43 -0
- package/skills/temporal-architect-design/reference/workflow-boundaries.md +43 -0
- package/skills/temporal-architect-design/topics/activities-advanced.md +358 -0
- package/skills/temporal-architect-design/topics/activities-advanced.twf +107 -0
- package/skills/temporal-architect-design/topics/child-workflows.md +347 -0
- package/skills/temporal-architect-design/topics/child-workflows.twf +171 -0
- package/skills/temporal-architect-design/topics/long-running.md +230 -0
- package/skills/temporal-architect-design/topics/long-running.twf +100 -0
- package/skills/temporal-architect-design/topics/nexus.md +248 -0
- package/skills/temporal-architect-design/topics/nexus.twf +148 -0
- package/skills/temporal-architect-design/topics/patterns.md +469 -0
- package/skills/temporal-architect-design/topics/patterns.twf +346 -0
- package/skills/temporal-architect-design/topics/promises-conditions.md +179 -0
- package/skills/temporal-architect-design/topics/promises-conditions.twf +213 -0
- package/skills/temporal-architect-design/topics/signals-queries-updates.md +319 -0
- package/skills/temporal-architect-design/topics/signals-queries-updates.twf +234 -0
- package/skills/temporal-architect-design/topics/task-queues.md +205 -0
- package/skills/temporal-architect-design/topics/task-queues.twf +184 -0
- package/skills/temporal-architect-design/topics/testing.md +437 -0
- package/skills/temporal-architect-design/topics/testing.twf +177 -0
- package/skills/temporal-architect-design/topics/timers-scheduling.md +131 -0
- package/skills/temporal-architect-design/topics/timers-scheduling.twf +129 -0
- package/skills/temporal-architect-design/topics/versioning.md +434 -0
- package/skills/temporal-architect-design/topics/versioning.twf +174 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Source: nexus.md
|
|
2
|
+
# Patterns: nexus service definitions, endpoint routing, cross-namespace calls
|
|
3
|
+
|
|
4
|
+
# --- Nexus service definition ---
|
|
5
|
+
|
|
6
|
+
nexus service PaymentsService:
|
|
7
|
+
async ProcessPayment workflow ProcessPaymentWorkflow
|
|
8
|
+
sync GetPaymentStatus(paymentId: string) -> (PaymentStatus):
|
|
9
|
+
activity LookupPayment(paymentId) -> status
|
|
10
|
+
close complete(status)
|
|
11
|
+
|
|
12
|
+
nexus service NotificationsService:
|
|
13
|
+
async SendConfirmation workflow SendConfirmationWorkflow
|
|
14
|
+
|
|
15
|
+
# --- Caller workflow using nexus ---
|
|
16
|
+
|
|
17
|
+
workflow OrderCheckout(order: Order) -> (OrderResult):
|
|
18
|
+
# Validate locally
|
|
19
|
+
activity ValidateCheckout(order)
|
|
20
|
+
|
|
21
|
+
# Call into payments via Nexus endpoint (child call)
|
|
22
|
+
nexus PaymentsEndpoint PaymentsService.ProcessPayment(order.payment) -> paymentResult
|
|
23
|
+
|
|
24
|
+
# Call into notifications (fire-and-forget)
|
|
25
|
+
detach nexus NotificationsEndpoint NotificationsService.SendConfirmation(order.customer, paymentResult)
|
|
26
|
+
|
|
27
|
+
close complete(OrderResult{paymentId: paymentResult.id})
|
|
28
|
+
|
|
29
|
+
# --- Async nexus call (promise) ---
|
|
30
|
+
|
|
31
|
+
workflow AsyncNexusCaller(data: Data) -> (Result):
|
|
32
|
+
# Start nexus call, get promise
|
|
33
|
+
promise paymentHandle <- nexus PaymentsEndpoint PaymentsService.ProcessPayment(data.payment)
|
|
34
|
+
|
|
35
|
+
# Do other work in parallel
|
|
36
|
+
activity DoLocalWork(data) -> localResult
|
|
37
|
+
|
|
38
|
+
# Wait for nexus result when needed
|
|
39
|
+
await paymentHandle -> paymentResult
|
|
40
|
+
|
|
41
|
+
close complete(Result{localResult, paymentResult})
|
|
42
|
+
|
|
43
|
+
# --- Nexus with options ---
|
|
44
|
+
|
|
45
|
+
workflow NexusWithOptions(payment: Payment) -> (PaymentResult):
|
|
46
|
+
nexus PaymentsEndpoint PaymentsService.ProcessPayment(payment) -> result
|
|
47
|
+
options:
|
|
48
|
+
schedule_to_close_timeout: 5m
|
|
49
|
+
close complete(PaymentResult{result})
|
|
50
|
+
|
|
51
|
+
# --- Nexus error handling with await one ---
|
|
52
|
+
|
|
53
|
+
workflow NexusWithTimeout(data: Data) -> (Result):
|
|
54
|
+
await one:
|
|
55
|
+
nexus PaymentsEndpoint PaymentsService.ProcessPayment(data) -> result:
|
|
56
|
+
close complete(Result{success: true, data: result})
|
|
57
|
+
timer(5m):
|
|
58
|
+
activity AlertTimeout(data)
|
|
59
|
+
close fail(Result{success: false, error: "timeout"})
|
|
60
|
+
|
|
61
|
+
# --- Supporting local activities ---
|
|
62
|
+
|
|
63
|
+
activity ValidateCheckout(order: Order):
|
|
64
|
+
validate(order)
|
|
65
|
+
|
|
66
|
+
activity DoLocalWork(data: Data) -> (LocalResult):
|
|
67
|
+
return process(data)
|
|
68
|
+
|
|
69
|
+
activity AlertTimeout(data: Data):
|
|
70
|
+
alert(data)
|
|
71
|
+
|
|
72
|
+
activity LookupPayment(paymentId: string) -> (PaymentStatus):
|
|
73
|
+
return lookup(paymentId)
|
|
74
|
+
|
|
75
|
+
# --- Target workflows ---
|
|
76
|
+
|
|
77
|
+
workflow ProcessPaymentWorkflow(payment: Payment) -> (PaymentResult):
|
|
78
|
+
activity ValidatePaymentDetails(payment)
|
|
79
|
+
activity ChargePaymentMethod(payment) -> result
|
|
80
|
+
activity RecordPaymentTransaction(result)
|
|
81
|
+
close complete(PaymentResult{id: result.id})
|
|
82
|
+
|
|
83
|
+
workflow SendConfirmationWorkflow(customer: Customer, paymentResult: PaymentResult):
|
|
84
|
+
activity SendPaymentConfirmation(customer.email, paymentResult)
|
|
85
|
+
close complete
|
|
86
|
+
|
|
87
|
+
# --- Supporting activities for target workflows ---
|
|
88
|
+
|
|
89
|
+
activity ValidatePaymentDetails(payment: Payment):
|
|
90
|
+
validate(payment)
|
|
91
|
+
|
|
92
|
+
activity ChargePaymentMethod(payment: Payment) -> (ChargeResult):
|
|
93
|
+
return charge(payment)
|
|
94
|
+
|
|
95
|
+
activity RecordPaymentTransaction(result: ChargeResult):
|
|
96
|
+
record(result)
|
|
97
|
+
|
|
98
|
+
activity SendPaymentConfirmation(email: string, data: PaymentResult):
|
|
99
|
+
send(email, data)
|
|
100
|
+
|
|
101
|
+
# --- Worker and namespace definitions ---
|
|
102
|
+
|
|
103
|
+
worker paymentProcessingWorker:
|
|
104
|
+
workflow ProcessPaymentWorkflow
|
|
105
|
+
activity ValidatePaymentDetails
|
|
106
|
+
activity ChargePaymentMethod
|
|
107
|
+
activity RecordPaymentTransaction
|
|
108
|
+
activity LookupPayment
|
|
109
|
+
nexus service PaymentsService
|
|
110
|
+
|
|
111
|
+
worker notificationWorker:
|
|
112
|
+
workflow SendConfirmationWorkflow
|
|
113
|
+
activity SendPaymentConfirmation
|
|
114
|
+
nexus service NotificationsService
|
|
115
|
+
|
|
116
|
+
worker checkoutWorker:
|
|
117
|
+
workflow OrderCheckout
|
|
118
|
+
workflow AsyncNexusCaller
|
|
119
|
+
workflow NexusWithOptions
|
|
120
|
+
workflow NexusWithTimeout
|
|
121
|
+
activity ValidateCheckout
|
|
122
|
+
activity DoLocalWork
|
|
123
|
+
activity AlertTimeout
|
|
124
|
+
|
|
125
|
+
# Three separate namespaces — the defining feature of Nexus.
|
|
126
|
+
# Endpoints live in the target namespace alongside the workers that serve them.
|
|
127
|
+
# The caller namespace (orders) just references endpoint names in its workflows.
|
|
128
|
+
|
|
129
|
+
namespace payments:
|
|
130
|
+
worker paymentProcessingWorker
|
|
131
|
+
options:
|
|
132
|
+
task_queue: "payments"
|
|
133
|
+
nexus endpoint PaymentsEndpoint
|
|
134
|
+
options:
|
|
135
|
+
task_queue: "payments"
|
|
136
|
+
|
|
137
|
+
namespace notifications:
|
|
138
|
+
worker notificationWorker
|
|
139
|
+
options:
|
|
140
|
+
task_queue: "notifications"
|
|
141
|
+
nexus endpoint NotificationsEndpoint
|
|
142
|
+
options:
|
|
143
|
+
task_queue: "notifications"
|
|
144
|
+
|
|
145
|
+
namespace orders:
|
|
146
|
+
worker checkoutWorker
|
|
147
|
+
options:
|
|
148
|
+
task_queue: "checkout"
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# Workflow Patterns
|
|
2
|
+
|
|
3
|
+
> **Example:** [`patterns.twf`](./patterns.twf)
|
|
4
|
+
|
|
5
|
+
Common patterns for structuring Temporal workflows. Choose based on your use case characteristics.
|
|
6
|
+
|
|
7
|
+
## Pattern Overview
|
|
8
|
+
|
|
9
|
+
| Pattern | Use When | Example |
|
|
10
|
+
|---------|----------|---------|
|
|
11
|
+
| **Process** | Discrete operation with start and end | Order fulfillment |
|
|
12
|
+
| **Entity** | Long-lived, represents a thing | User account |
|
|
13
|
+
| **Saga** | Distributed transaction with compensation | Multi-service booking |
|
|
14
|
+
| **Fan-Out/Fan-In** | Parallel processing with aggregation | Batch processing |
|
|
15
|
+
| **Pipeline** | Sequential stages of transformation | Data processing |
|
|
16
|
+
| **State Machine** | Explicit state transitions | Document approval |
|
|
17
|
+
| **Polling** | Wait for external condition | Resource provisioning |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Process Workflow
|
|
22
|
+
|
|
23
|
+
A discrete operation that drives toward completion.
|
|
24
|
+
|
|
25
|
+
### Characteristics
|
|
26
|
+
- Has a clear start and end
|
|
27
|
+
- Progresses through defined steps
|
|
28
|
+
- Returns a result when complete
|
|
29
|
+
- Relatively short-lived (minutes to hours)
|
|
30
|
+
|
|
31
|
+
### Pattern
|
|
32
|
+
|
|
33
|
+
```twf
|
|
34
|
+
workflow OrderFulfillment(order: Order) -> (OrderResult):
|
|
35
|
+
# Step 1: Validate
|
|
36
|
+
activity ValidateRetailOrder(order) -> validated
|
|
37
|
+
if not validated.success:
|
|
38
|
+
close fail(OrderResult{status: "invalid", error: validated.error})
|
|
39
|
+
|
|
40
|
+
# Step 2: Reserve
|
|
41
|
+
activity ReserveInventory(order.items) -> reservation
|
|
42
|
+
|
|
43
|
+
# Step 3: Charge
|
|
44
|
+
activity ProcessPayment(order.payment) -> payment
|
|
45
|
+
|
|
46
|
+
# Step 4: Fulfill
|
|
47
|
+
activity ShipRetailOrder(order, reservation)
|
|
48
|
+
|
|
49
|
+
# Step 5: Notify
|
|
50
|
+
activity SendConfirmation(order.customer)
|
|
51
|
+
|
|
52
|
+
close complete(OrderResult{status: "completed", trackingId: reservation.trackingId})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### When to Use
|
|
56
|
+
- Order processing
|
|
57
|
+
- User registration
|
|
58
|
+
- Deployment pipelines
|
|
59
|
+
- Report generation
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Entity Workflow
|
|
64
|
+
|
|
65
|
+
A long-running workflow representing a business entity.
|
|
66
|
+
|
|
67
|
+
### Characteristics
|
|
68
|
+
- Long-lived (days, months, indefinitely)
|
|
69
|
+
- Reacts to external events (signals)
|
|
70
|
+
- Maintains state over time
|
|
71
|
+
- Uses continue-as-new to manage history
|
|
72
|
+
|
|
73
|
+
### Pattern
|
|
74
|
+
|
|
75
|
+
Handlers (`signal`/`query`/`update`) are declared **before** the workflow body, not after it. `history_length()` returns an event *count* (use it against an event-count threshold); `history_size()` returns *bytes* (use it against a byte limit) — don't confuse the two.
|
|
76
|
+
|
|
77
|
+
```twf
|
|
78
|
+
workflow AccountEntity(accountId: string, account: Account):
|
|
79
|
+
query GetBalance() -> (decimal):
|
|
80
|
+
return account.balance
|
|
81
|
+
|
|
82
|
+
update Transfer(amount: decimal, toAccount: string) -> (TransferResult):
|
|
83
|
+
if account.balance < amount:
|
|
84
|
+
return TransferResult{success: false, error: "insufficient funds"}
|
|
85
|
+
account.balance -= amount
|
|
86
|
+
activity InitiateTransfer(toAccount, amount)
|
|
87
|
+
return TransferResult{success: true}
|
|
88
|
+
|
|
89
|
+
if account == null:
|
|
90
|
+
activity LoadAccount(accountId) -> account
|
|
91
|
+
|
|
92
|
+
for:
|
|
93
|
+
await one:
|
|
94
|
+
signal Deposit:
|
|
95
|
+
account.balance += signal.amount
|
|
96
|
+
activity RecordAccountTransaction(accountId, "deposit", signal.amount)
|
|
97
|
+
|
|
98
|
+
signal Withdraw:
|
|
99
|
+
if account.balance >= signal.amount:
|
|
100
|
+
account.balance -= signal.amount
|
|
101
|
+
activity RecordAccountTransaction(accountId, "withdraw", signal.amount)
|
|
102
|
+
|
|
103
|
+
signal Close:
|
|
104
|
+
activity CloseAccount(accountId)
|
|
105
|
+
close complete
|
|
106
|
+
|
|
107
|
+
timer(24h):
|
|
108
|
+
activity DailyReconciliation(accountId, account)
|
|
109
|
+
|
|
110
|
+
# history_length() = event count; reset before it grows large
|
|
111
|
+
if history_length() > 1000:
|
|
112
|
+
close continue_as_new(accountId, account)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### When to Use
|
|
116
|
+
- User accounts
|
|
117
|
+
- Subscriptions
|
|
118
|
+
- Shopping carts
|
|
119
|
+
- IoT device state
|
|
120
|
+
- Game sessions
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Saga Pattern
|
|
125
|
+
|
|
126
|
+
Distributed transaction with compensation for failures.
|
|
127
|
+
|
|
128
|
+
### Characteristics
|
|
129
|
+
- Multiple services/steps that must all succeed or all roll back
|
|
130
|
+
- Each step has a compensating action
|
|
131
|
+
- Compensation runs in reverse order on failure
|
|
132
|
+
- Provides eventual consistency
|
|
133
|
+
|
|
134
|
+
### Pattern
|
|
135
|
+
|
|
136
|
+
> Note: The saga pattern requires error-handling constructs (try/catch, compensation stacks) that are expressed here as conceptual pseudo-code. See [`patterns.twf`](./patterns.twf) for the TWF syntax version.
|
|
137
|
+
|
|
138
|
+
```pseudo
|
|
139
|
+
workflow BookingWorkflow(booking: Booking) -> BookingResult:
|
|
140
|
+
# Step 1: Reserve flight
|
|
141
|
+
activity ReserveFlight(booking.flight) -> flight
|
|
142
|
+
|
|
143
|
+
# Step 2: Reserve hotel (compensate flight on failure)
|
|
144
|
+
activity ReserveHotel(booking.hotel) -> hotel
|
|
145
|
+
# on failure: activity CancelFlight(flight.id)
|
|
146
|
+
|
|
147
|
+
# Step 3: Reserve car (compensate hotel + flight on failure)
|
|
148
|
+
activity ReserveCar(booking.car) -> car
|
|
149
|
+
# on failure: activity CancelHotel(hotel.id), activity CancelFlight(flight.id)
|
|
150
|
+
|
|
151
|
+
# Step 4: Charge payment (compensate all on failure)
|
|
152
|
+
activity ChargeBookingPayment(booking.payment) -> payment
|
|
153
|
+
# on failure: activity CancelCar(car.id), CancelHotel(...), CancelFlight(...)
|
|
154
|
+
|
|
155
|
+
# All succeeded
|
|
156
|
+
close complete(BookingResult{status: "confirmed", flight, hotel, car, payment})
|
|
157
|
+
|
|
158
|
+
# On any step failure, compensations run in reverse order
|
|
159
|
+
# SDK-level error handling drives the compensation logic
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Compensation Design
|
|
163
|
+
|
|
164
|
+
| Step | Forward Action | Compensation |
|
|
165
|
+
|------|---------------|--------------|
|
|
166
|
+
| Reserve | Create pending reservation | Cancel reservation |
|
|
167
|
+
| Charge | Process payment | Refund payment |
|
|
168
|
+
| Ship | Create shipment | Cancel shipment |
|
|
169
|
+
| Provision | Create resource | Delete resource |
|
|
170
|
+
|
|
171
|
+
### When to Use
|
|
172
|
+
- Multi-service transactions
|
|
173
|
+
- Booking systems (travel, events)
|
|
174
|
+
- Financial operations
|
|
175
|
+
- Resource provisioning
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Fan-Out/Fan-In Pattern
|
|
180
|
+
|
|
181
|
+
Process items in parallel, aggregate results.
|
|
182
|
+
|
|
183
|
+
### Characteristics
|
|
184
|
+
- Split work into parallel tasks
|
|
185
|
+
- Each task executes independently
|
|
186
|
+
- Aggregate results when all complete
|
|
187
|
+
- Handle partial failures
|
|
188
|
+
|
|
189
|
+
### Pattern
|
|
190
|
+
|
|
191
|
+
> Note: The TWF DSL currently re-binds the result variable on each iteration of `await all: for`. The aggregation step below is expressed as conceptual pseudo-code. See [`patterns.twf`](./patterns.twf) for the TWF syntax version.
|
|
192
|
+
|
|
193
|
+
```twf
|
|
194
|
+
workflow BatchProcessor(items: []Item) -> (BatchResult):
|
|
195
|
+
# Fan-out: start all processing in parallel
|
|
196
|
+
await all:
|
|
197
|
+
for (item in items):
|
|
198
|
+
activity ProcessItem(item) -> result
|
|
199
|
+
|
|
200
|
+
# Fan-in: aggregate results (conceptual -- SDK collects results)
|
|
201
|
+
activity AggregateResults(items) -> aggregated
|
|
202
|
+
|
|
203
|
+
close complete(BatchResult{results: aggregated})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Variations
|
|
207
|
+
|
|
208
|
+
**With Concurrency Limit:**
|
|
209
|
+
```twf
|
|
210
|
+
workflow RateLimitedBatch(items: []Item) -> (BatchResult):
|
|
211
|
+
# Process in batches of 10
|
|
212
|
+
for (batch in chunk(items, 10)):
|
|
213
|
+
await all:
|
|
214
|
+
for (item in batch):
|
|
215
|
+
activity ProcessItem(item) -> result
|
|
216
|
+
|
|
217
|
+
close complete(BatchResult{})
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**With Result Selection:**
|
|
221
|
+
```twf
|
|
222
|
+
workflow FirstSuccessful(sources: []Source) -> (Data):
|
|
223
|
+
await all:
|
|
224
|
+
for (source in sources):
|
|
225
|
+
activity TryFetch(source) -> result
|
|
226
|
+
|
|
227
|
+
# SDK-level: find first successful result
|
|
228
|
+
activity FindFirstSuccess(sources) -> data
|
|
229
|
+
close complete(data)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### When to Use
|
|
233
|
+
- Batch processing
|
|
234
|
+
- Parallel API calls
|
|
235
|
+
- Distributed computation
|
|
236
|
+
- Report aggregation
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Pipeline Pattern
|
|
241
|
+
|
|
242
|
+
Sequential transformation stages.
|
|
243
|
+
|
|
244
|
+
### Characteristics
|
|
245
|
+
- Data flows through ordered stages
|
|
246
|
+
- Each stage transforms and passes to next
|
|
247
|
+
- Clear separation of concerns
|
|
248
|
+
- Easy to add/remove stages
|
|
249
|
+
|
|
250
|
+
### Pattern
|
|
251
|
+
|
|
252
|
+
```twf
|
|
253
|
+
workflow DataPipeline(rawData: RawData) -> (ProcessedData):
|
|
254
|
+
# Stage 1: Ingest
|
|
255
|
+
activity Ingest(rawData) -> ingested
|
|
256
|
+
|
|
257
|
+
# Stage 2: Validate
|
|
258
|
+
activity Validate(ingested) -> validated
|
|
259
|
+
if not validated.valid:
|
|
260
|
+
close fail(ProcessedData{status: "invalid", errors: validated.errors})
|
|
261
|
+
|
|
262
|
+
# Stage 3: Transform
|
|
263
|
+
activity Transform(validated.data) -> transformed
|
|
264
|
+
|
|
265
|
+
# Stage 4: Enrich
|
|
266
|
+
activity Enrich(transformed) -> enriched
|
|
267
|
+
|
|
268
|
+
# Stage 5: Load
|
|
269
|
+
activity Load(enriched)
|
|
270
|
+
|
|
271
|
+
close complete(ProcessedData{status: "complete", recordCount: enriched.count})
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### With Conditional Stages
|
|
275
|
+
|
|
276
|
+
```twf
|
|
277
|
+
workflow AdaptivePipeline(data: Data) -> (Result):
|
|
278
|
+
activity Parse(data) -> processed
|
|
279
|
+
|
|
280
|
+
if processed.needsEnrichment:
|
|
281
|
+
activity Enrich(processed) -> processed
|
|
282
|
+
|
|
283
|
+
if processed.format == "legacy":
|
|
284
|
+
activity ConvertLegacy(processed) -> processed
|
|
285
|
+
|
|
286
|
+
activity Finalize(processed) -> result
|
|
287
|
+
close complete(result)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### When to Use
|
|
291
|
+
- ETL processes
|
|
292
|
+
- Document processing
|
|
293
|
+
- Media transcoding
|
|
294
|
+
- Data migrations
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## State Machine Pattern
|
|
299
|
+
|
|
300
|
+
Explicit states and transitions.
|
|
301
|
+
|
|
302
|
+
### Characteristics
|
|
303
|
+
- Well-defined states
|
|
304
|
+
- Explicit transition rules
|
|
305
|
+
- Events trigger transitions
|
|
306
|
+
- Clear audit trail
|
|
307
|
+
|
|
308
|
+
### Pattern
|
|
309
|
+
|
|
310
|
+
```twf
|
|
311
|
+
workflow DocumentApproval(doc: Document) -> (ApprovalResult):
|
|
312
|
+
signal Submit():
|
|
313
|
+
phase = "pending_review"
|
|
314
|
+
activity NotifyReviewers(doc)
|
|
315
|
+
|
|
316
|
+
signal Approve():
|
|
317
|
+
phase = "approved"
|
|
318
|
+
|
|
319
|
+
signal Reject():
|
|
320
|
+
phase = "rejected"
|
|
321
|
+
|
|
322
|
+
signal RequestChanges():
|
|
323
|
+
phase = "changes_requested"
|
|
324
|
+
|
|
325
|
+
signal Withdraw():
|
|
326
|
+
phase = "withdrawn"
|
|
327
|
+
|
|
328
|
+
query GetPhase() -> (string):
|
|
329
|
+
return phase
|
|
330
|
+
|
|
331
|
+
phase = "draft"
|
|
332
|
+
|
|
333
|
+
for:
|
|
334
|
+
switch (phase):
|
|
335
|
+
case "draft":
|
|
336
|
+
await one:
|
|
337
|
+
signal Submit:
|
|
338
|
+
timer(90d):
|
|
339
|
+
phase = "expired"
|
|
340
|
+
case "pending_review":
|
|
341
|
+
await one:
|
|
342
|
+
signal Approve:
|
|
343
|
+
signal Reject:
|
|
344
|
+
signal RequestChanges:
|
|
345
|
+
timer(30d):
|
|
346
|
+
phase = "expired"
|
|
347
|
+
case "changes_requested":
|
|
348
|
+
await one:
|
|
349
|
+
signal Submit:
|
|
350
|
+
signal Withdraw:
|
|
351
|
+
timer(30d):
|
|
352
|
+
phase = "expired"
|
|
353
|
+
case "approved":
|
|
354
|
+
activity PublishDocument(doc)
|
|
355
|
+
close complete(ApprovalResult{status: "approved"})
|
|
356
|
+
case "rejected":
|
|
357
|
+
activity ArchiveDocument(doc)
|
|
358
|
+
close complete(ApprovalResult{status: "rejected"})
|
|
359
|
+
case "withdrawn":
|
|
360
|
+
close complete(ApprovalResult{status: "withdrawn"})
|
|
361
|
+
case "expired":
|
|
362
|
+
close complete(ApprovalResult{status: "expired"})
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### State Transition Table
|
|
366
|
+
|
|
367
|
+
| From State | Event | To State | Action |
|
|
368
|
+
|------------|-------|----------|--------|
|
|
369
|
+
| draft | Submit | pending_review | Notify reviewers |
|
|
370
|
+
| pending_review | Approve | approved | None |
|
|
371
|
+
| pending_review | Reject | rejected | None |
|
|
372
|
+
| pending_review | RequestChanges | changes_requested | None |
|
|
373
|
+
| changes_requested | Submit | pending_review | None |
|
|
374
|
+
| changes_requested | Withdraw | withdrawn | None |
|
|
375
|
+
| draft | Timer(90d) | expired | Auto-expire |
|
|
376
|
+
| pending_review | Timer(30d) | expired | Auto-expire |
|
|
377
|
+
| changes_requested | Timer(30d) | expired | Auto-expire |
|
|
378
|
+
|
|
379
|
+
### When to Use
|
|
380
|
+
- Approval workflows
|
|
381
|
+
- Order status tracking
|
|
382
|
+
- Support tickets
|
|
383
|
+
- Insurance claims
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Polling Pattern
|
|
388
|
+
|
|
389
|
+
Wait for external condition to be met.
|
|
390
|
+
|
|
391
|
+
### Characteristics
|
|
392
|
+
- External system doesn't push updates
|
|
393
|
+
- Must poll periodically
|
|
394
|
+
- Need backoff strategy
|
|
395
|
+
- Has timeout/deadline
|
|
396
|
+
|
|
397
|
+
### Pattern
|
|
398
|
+
|
|
399
|
+
```twf
|
|
400
|
+
workflow AwaitResourceReady(resourceId: string) -> (Resource):
|
|
401
|
+
backoff = 5s
|
|
402
|
+
maxBackoff = 60s
|
|
403
|
+
|
|
404
|
+
for:
|
|
405
|
+
activity CheckResourceStatus(resourceId) -> status
|
|
406
|
+
|
|
407
|
+
if status.ready:
|
|
408
|
+
activity GetResource(resourceId) -> resource
|
|
409
|
+
close complete(resource)
|
|
410
|
+
|
|
411
|
+
if status.failed:
|
|
412
|
+
activity CancelProvisioning(resourceId)
|
|
413
|
+
close fail(ProvisioningError{error: status.error})
|
|
414
|
+
|
|
415
|
+
# Wait with backoff, deadline via await one + timer
|
|
416
|
+
await one:
|
|
417
|
+
timer(backoff):
|
|
418
|
+
backoff = min(backoff * 2, maxBackoff)
|
|
419
|
+
timer(30m):
|
|
420
|
+
activity CancelProvisioning(resourceId)
|
|
421
|
+
close fail(ProvisioningTimeout{})
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### With Progress Updates
|
|
425
|
+
|
|
426
|
+
> Note: `upsert_search_attributes` is an SDK-level call, not TWF notation.
|
|
427
|
+
|
|
428
|
+
```twf
|
|
429
|
+
workflow MonitorJob(jobId: string) -> (JobResult):
|
|
430
|
+
for:
|
|
431
|
+
activity GetJobStatus(jobId) -> status
|
|
432
|
+
|
|
433
|
+
# Update search attributes for visibility (SDK call)
|
|
434
|
+
# upsert_search_attributes({JobProgress: status.percentComplete, JobStage: status.currentStage})
|
|
435
|
+
|
|
436
|
+
if status.complete:
|
|
437
|
+
activity GetJobResult(jobId) -> result
|
|
438
|
+
close complete(result)
|
|
439
|
+
|
|
440
|
+
await timer(30s)
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### When to Use
|
|
444
|
+
- Resource provisioning
|
|
445
|
+
- External job monitoring
|
|
446
|
+
- Third-party integrations
|
|
447
|
+
- CI/CD pipelines
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Pattern Selection Guide
|
|
452
|
+
|
|
453
|
+
```text
|
|
454
|
+
Start
|
|
455
|
+
│
|
|
456
|
+
├─ Is it a long-lived entity? ──────────────► Entity Pattern
|
|
457
|
+
│
|
|
458
|
+
├─ Does it need distributed rollback? ──────► Saga Pattern
|
|
459
|
+
│
|
|
460
|
+
├─ Can items be processed in parallel? ─────► Fan-Out/Fan-In
|
|
461
|
+
│
|
|
462
|
+
├─ Is it a series of transformations? ──────► Pipeline Pattern
|
|
463
|
+
│
|
|
464
|
+
├─ Are there explicit states/transitions? ──► State Machine
|
|
465
|
+
│
|
|
466
|
+
├─ Need to wait for external condition? ────► Polling Pattern
|
|
467
|
+
│
|
|
468
|
+
└─ Simple start-to-finish process? ─────────► Process Workflow
|
|
469
|
+
```
|