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
ScheduleCronSchedule / DistributedCronCadence uses CronSchedule on workflow options or the distributed cron pattern
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 := worker.New(cadenceClient, "your-domain", "my-task-list", worker.Options{})

Note that the Cadence worker constructor 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

// 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