Skip to main content

Migrating from Temporal to Cadence

Cadence and Temporal share the same durable execution model; Temporal is a 2019 fork of Cadence. Core concepts map directly, but the two projects have diverged in naming conventions, SDK packages, and client setup. This page covers what needs to change when moving a Go or Java application from Temporal to Cadence.

Concept mapping​

TemporalCadenceNotes
NamespaceDomainTop-level isolation boundary for workflows
TaskQueueTaskListRoutes work to the right workers
WorkerWorkerSame concept, different SDK packages
WorkflowWorkflowSame concept
ActivityActivitySame concept
SignalSignalSame concept
QueryQuerySame concept
ScheduleSchedule / CronScheduleCadence has a first-class Schedules API; CronSchedule on StartWorkflowOptions is also still supported
workflow.GetLoggerworkflow.GetLoggerSame API
temporal.io/sdkgo.uber.org/cadenceGo package root
io.temporalcom.uber.cadenceJava package root

Go SDK​

Package imports​

// Temporal
import (
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"
"go.temporal.io/sdk/workflow"
"go.temporal.io/sdk/activity"
)

// Cadence
import (
"go.uber.org/cadence/client"
"go.uber.org/cadence/worker"
"go.uber.org/cadence/workflow"
"go.uber.org/cadence/activity"
)

Client setup​

Cadence uses YARPC for transport rather than gRPC. The client setup is different:

// Cadence
import (
"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient"
"go.uber.org/yarpc"
"go.uber.org/yarpc/transport/tchannel"
)

ch, _ := tchannel.NewChannelTransport(tchannel.ServiceName("cadence-client"))
dispatcher := yarpc.NewDispatcher(yarpc.Config{
Name: "cadence-client",
Outbounds: yarpc.Outbounds{
"cadence-frontend": {Unary: ch.NewSingleOutbound("127.0.0.1:7933")},
},
})
dispatcher.Start()

cadenceClient := workflowserviceclient.New(dispatcher.ClientConfig("cadence-frontend"))
c, _ := client.NewClient(cadenceClient, "your-domain", &client.Options{})

Starting a workflow​

// Temporal
options := client.StartWorkflowOptions{
TaskQueue: "my-task-queue",
}

// Cadence
options := client.StartWorkflowOptions{
TaskList: "my-task-list",
}

Worker registration​

// Temporal
w := worker.New(c, "my-task-queue", worker.Options{})

// Cadence
w, err := worker.NewV2(cadenceClient, "your-domain", "my-task-list", worker.Options{})
if err != nil {
return err
}

Note that the Cadence worker constructor returns an initialization error and takes the domain name as an explicit argument.

Java SDK​

Package imports​

// Temporal
import io.temporal.client.WorkflowClient;
import io.temporal.client.WorkflowOptions;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;
import io.temporal.workflow.Workflow;
import io.temporal.activity.Activity;

// Cadence
import com.uber.cadence.client.WorkflowClient;
import com.uber.cadence.client.WorkflowClientOptions;
import com.uber.cadence.client.WorkflowOptions;
import com.uber.cadence.serviceclient.ClientOptions;
import com.uber.cadence.serviceclient.IWorkflowService;
import com.uber.cadence.serviceclient.WorkflowServiceTChannel;
import com.uber.cadence.worker.Worker;
import com.uber.cadence.worker.WorkerFactory;
import com.uber.cadence.workflow.Workflow;
import com.uber.cadence.activity.Activity;

Client setup​

note

WorkflowServiceTChannel applies to the 3.x Java client. The 4.x client removed TChannel and Thrift; use WorkflowServiceGrpc instead.

// Cadence
IWorkflowService service = new WorkflowServiceTChannel(ClientOptions.defaultInstance());
WorkflowClient client = WorkflowClient.newInstance(service,
WorkflowClientOptions.newBuilder().setDomain("your-domain").build());

Starting a workflow​

// Temporal
WorkflowOptions options = WorkflowOptions.newBuilder()
.setTaskQueue("my-task-queue")
.build();

// Cadence
WorkflowOptions options = new WorkflowOptions.Builder()
.setTaskList("my-task-list")
.build();

What stays the same​

Workflow and activity implementation code is nearly identical between the two SDKs. The programming model, determinism constraints, signal/query patterns, child workflows, and retry policies all work the same way. Most of the migration work is in the client/worker setup and the namespace→domain and task-queue→task-list renames.

Further reading​