Temporal Workflow Design in Practice: Modeling a Construction Worker’s Full Shift with Signals, Updates, and Idempotency
The most important architectural decision in this system was not which database to use, or how to structure the API, or how to handle offline sync. It was simpler and more consequential than any of those: treat the worker’s shift as one workflow.
Not one workflow per operation. Not one workflow per service boundary. One workflow per worker per shift — owning the complete state of that shift from clock-in to checkout, with every event in between processed in validated sequence.
Everything else follows from that decision.
The Shift Lifecycle as a Business Process
Before writing a line of Temporal code, it is worth mapping what the shift lifecycle actually contains — because the workflow needs to own all of it.
Clock-in phase:
- Worker initiates clock-in via the app
- System confirms presence and starts payroll clock
- Safety documents are presented for signature — confined space permits, equipment authorisations, site-specific inductions
- Pre-work compliance review — fit for work confirmation, hazard acknowledgement, PPE verification
- Qualification check against tasks assigned for the shift
Active shift phase:
- Tool checkouts and returns
- Work progress recording
- Schedule updates from supervisors
- Incident report filing if required
- Document corrections if something was entered incorrectly
- Late-arriving submissions from periods of offline operation
Checkout phase:
- Checkout initiated
- Hours calculation and sub-job allocation
- ERP handoff for payroll processing
- Compliance record closure
- Workflow completion
That is one business process. It has a defined start, a defined end, a sequence of states it moves through, and a set of events that can arrive at any point during the active phase — including bursts of queued events from an offline period.
Modelling it as anything other than a single workflow means distributing ownership of the sequence across multiple services — and losing the ability to know, at any given moment, exactly what state the shift is in.
Workflow Structure: What Goes Where
In Temporal, the workflow code is the orchestration logic. It decides what happens next, maintains state, and handles transitions. Activities are the side effects — the operations that actually touch the outside world.
What lives in the workflow:
- Current shift state (clocked in, compliance pending, active, checking out, complete)
- Event queue and processing order
- Transition validation logic
- Timer management for escalations and reminders
- Signal and update handlers
What lives in activities:
- Signature capture and PDF generation
- Database writes for shift records
- ERP integration calls
- Document service interactions
- Notification dispatch
This boundary matters. Workflow code in Temporal must be deterministic — it cannot make network calls, generate random values, or behave differently on replay. Activities are where non-determinism lives. Keeping this boundary clean means the workflow stays readable and the activities handle their own reliability concerns independently.
Temporal Signals vs Updates: The Decision Framework.
When a worker submits something through the app — a signature, a compliance acknowledgement, a tool checkout — that translates into a workflow interaction. The choice between a signal and an update is one of the most consequential design decisions in the system.
Use an update when the user is waiting for a response.
When a worker submits their safety document signature, they cannot proceed to the next compliance step until the system confirms the submission was accepted. The app needs to block and wait. That is a Temporal update — the app sends the request, the workflow validates the submission and returns a success or a specific error, the app unblocks and shows the worker what to do next.
Updates give you a request-response interaction inside a durable workflow. The mobile app gets immediate feedback. The worker is not left staring at a spinner while the system processes asynchronously.
Use a signal when nothing is blocking on the response.
When a supervisor assigns a tool to a worker, the worker’s workflow needs to know about it — but the supervisor does not need to wait for the worker’s workflow to process the assignment before continuing. That is a signal: sent to the worker’s workflow, guaranteed delivery, but the supervisor’s action completes immediately.
Background events follow the same pattern. A service flagging that a document has been archived, an ERP system confirming that the shift record has been picked up — these are signals. The workflow is informed, it updates its state, nobody is waiting on a response.
The framework is straightforward: updates for anything the user is waiting on, signals for everything the workflow needs to know about but that is not blocking a user action.
The Internal Queue: Why Arrival Order Is Not Processing Order
Temporal guarantees durable delivery of signals. It does not guarantee that the order signals arrive maps to the order the business process needs them applied.
In a field environment, this is a real problem. A worker submits several actions while offline. They reconnect. The app sends a burst of queued events. The order they arrive at the server depends on network conditions, retry timing, and the order the app chose to send them — not necessarily the order they happened.
The solution is an internal request queue inside the workflow. Events arrive as signals, are placed on the queue with request IDs and timestamps, and the workflow processes them one at a time in validated sequence. Before processing each event, the workflow checks the transition against the current state — if the transition is not valid from the current state, the event is rejected with a specific reason.
This gives the system control over processing order independently of arrival order, which is the correct model for a compliance-critical process where sequence is part of the meaning.
Idempotency: Not All Duplicate Events Are Equal
Bad connections in field operations are not edge cases. They are a daily operational reality. A worker submits a form, the network drops, the app retries. Now two identical submissions arrive at the workflow.
The naive solution is to deduplicate by request ID — if you have already processed a request with this ID, return the cached result and do nothing. That is correct for most events. But it is not the right model for all events.
The critical distinction is between two categories of event:
Latest supersedes events. Pre-work compliance submissions, questionnaire responses, form acknowledgements. If a worker submits the same compliance form twice because of a flaky connection, you take the latest version and discard the older one. The last known state is the truth. Processing both would be wrong — it might trigger duplicate notifications, duplicate records, or incorrect state transitions.
Sequence-authoritative events. Clock-in and checkout. These are not idempotent in the same sense. They are authoritative transitions where the sequence is part of the meaning. A checkout signal must be processed after the clock-in. You cannot skip it, you cannot reorder it, and you cannot substitute the latest version for the earlier one — they are not equivalent submissions of the same form, they are two different events in a defined sequence.
The workflow handles these categories differently at the signal level. Different event types have different names, and the handler logic for each category encodes the right rule. For latest-supersedes events, if multiple pending submissions of the same type are queued, the workflow takes the latest and drops the older ones. For sequence-authoritative events, strict ordering is enforced regardless of arrival order.
This distinction — encoding business semantics into the idempotency model rather than applying a single deduplication rule uniformly — is one of the decisions that makes the system correct rather than merely consistent.
The Fallback Pattern: When Updates Cannot Wait
There is a timing problem that occurs at the boundary between updates and signals. When the app sends an update and the network drops immediately after the submission lands, the app does not receive a response. The update went through — the workflow processed it — but the app does not know that.
The app cannot wait indefinitely. So it falls back to sending a signal for the same action — fire and forget, guaranteed delivery.
Now both arrive: the update was processed, and the fallback signal is in the queue for the same action.
The workflow handles this with request ID tracking. Every request carries a request ID generated by the app at the point of action. The workflow tracks which request IDs it has already processed. When a duplicate arrives — same request ID, regardless of whether it came via update or signal — the workflow returns the cached result without re-executing the action.
This means the fallback pattern is safe. The app can always fall back to a signal when an update times out, and the workflow will deduplicate correctly regardless of which path the request came through.
What Observability Looks Like With This Model
Before Temporal, debugging a worker’s bad record meant querying four or five different databases, cross-referencing timestamps, trying to reconstruct a sequence of events from logs written in separate services with no shared correlation ID. An hour of investigation before you could identify what went wrong.
With the workflow model, debugging is inspection rather than investigation. You open the workflow history for that worker’s shift. You see every event in order, with timestamps, with the inputs and outputs of every activity, with the state of the workflow at each step.
If a checkout resulted in wrong hours, you can see exactly what the workflow knew at the point it calculated those hours. The history is not a log — it is a complete, ordered, replayable record of the business process.
For the client’s regulatory obligations, this is core product value, not a debugging convenience. When a regulator asks for proof that a worker completed a safety acknowledgement before entering a hazardous area, the workflow history provides a verifiable, tamper-evident answer.
The Decision That Makes the Smaller Decisions Easier
Early in the project there were discussions about using Temporal for individual operations only — just the PDF signing, just the ERP write — rather than the whole lifecycle. The argument for doing it partially made sense on the surface: lower risk, smaller change.
But it would have missed the point. The problem was not that individual operations were unreliable in isolation. The problem was that nobody was owning the sequence. The shift is one business process. It needed one process owner.
Once you commit to modelling the shift as one workflow, the right answers to the smaller questions — how to handle duplicate events, where to put idempotency logic, how to validate state transitions, how to structure the audit record — become significantly clearer. The constraints of the model guide the design.
One process. One owner. One durable history.
Building for environments where failure is expensive? Watch how we did it — no slides, no theory, just the real architecture. https://youtu.be/gZKhpTCDPUQ

