Temporal and Actor Model

Temporal follows Actor Model to create workflows that are just plain code functions and plain actors.

Temporal is an amazing technology. It allows you to create workflows in plain code. You can write microservices in different languages, and orchestrate them in another one.

Temporal is based on the Actor Model. The Actor Model is a mathematical model that makes actors a first-class citizen to achieve concurrency. Actors communicate with each other through messages. In response to a message, an actor can do three things:

  • Update its internal state
  • Spawn new actors
  • Send messages to other actors

Each actor is absolutely independent and its state can only be modified in reaction to a recognized and authorized message. It’s a perfect model to write concurrent programs because actors never share memory, they communicate. Therefore, two actors, part of a common system, can live in totally different world regions, provided that they can communicate between each other.

The actor model is more than a way to create concurrent programs, it’s a way to think problems in term of absolutely independent entities. Links between actors don’t exist by default. You can’t access the local state of an actor without the actor explicitly creating this link.

Let’s see how Temporal implements the Actor Model, in the context of their TypeScript SDK.

permalinkUpdate internal state

Temporal workflows are functions. All variables declared in the function scope will be automatically persisted by Temporal Server, so that the workflow can come back to its state if the worker that ran it crashed! You can use any construct from SDK language inside a Temporal workflow, at least with TypeScript SDK.

Signals, in Temporal terminology, are messages that trigger mutations. In response to a signal, we can update the internal state.

ts
import { defineSignal, setHandler } from '@temporalio/workflow';
export const addTrackSignal = defineSignal<[string]>('addTrack');
export async function tracksListWorkflow() {
const tracks = new Set();
setHandler(addTrackSignal, (trackToAdd) => {
tracks.add(trackToAdd);
});
}

permalinkSpawn new actors

Temporal workflows can spawn child workflows. It’s a great feature to model hierarchical relationships. You can use this feature to parallelize tasks, orchestrated individually by child workflows.

ts
import { startChild } from '@temporalio/workflow';
export async function spawnChildWorkflow() {
const childHandle = await startChild(computeSomeTaskWorkflow, {
args: ['childWorkflowArgs'],
});
// Use childHandle to query the child workflow, send to it a signal
// or wait for its result.
}

permalinkSend messages between actors

As we saw, workflows can receive signals. Signals can be sent from the outside of workflows, thanks to a Temporal client, or from a workflow to another workflow. Let’s focus on inter-workflows communication. It can occur between a workflow and one of its child workflow, or a random workflow.

permalinkSend messages to child actor

When we spawn a child workflow, we get a handle that we can use to interact with it. This handle becomes part of workflow internal state.

ts
import { startChild } from '@temporalio/workflow';
export async function spawnChildWorkflow() {
const childHandle = await startChild(computeSomeTaskWorkflow, {
args: ['childWorkflowArgs'],
});
await childHandle.signal('someSignal');
}

permalinkSend messages to random actor

If we know its workflowId, we can interact with any external workflow. In the Actor Model, actors don’t know about each other instinctively. They live on an island in the middle of an ocean. They need to be told about the existence of an actor, that is, getting its address, to communicate with it. It really looks like the real world, where everything and everybody is an actor.

ts
import { getExternalWorkflowHandle, workflowInfo } from '@temporalio/workflow';
export async function signalExternalWorkflow(externalWorkflowId: string) {
const externalHandle = await getExternalWorkflowHandle(externalWorkflowId);
await externalHandle.signal('someSignal');
}

permalinkQueries vs Signals

Temporal makes a distinction between messages that are meant to extract some data from the internal state of an actor, queries, and messages that modify it, signals.

permalinkConclusion

Temporal is amazing. Go check it and try it! The TypeScript SDK has a fabulous DX.

As Temporal has been built with Actor Model in mind, workflows are meant to work in isolation from others, and need to establish strong contracts to receive messages from the outside. At the end, being explicit always wins!

Join my mailing list

Get monthly insights, personal stories, and in-depth information about what I find helpful in web development as a freelancer.

Email advice once a month + Free XState report + Free course on XState

Want to see what it looks like? View the previous issues.

I value your privacy and will never share your email address.