Skip to main content

Workflow basics - Go SDK

How to develop a basic Workflow

Workflows are the fundamental unit of a Temporal Application, and it all starts with the development of a Workflow Definition.

In the Temporal Go SDK programming model, a Workflow Definition is an exportable function. Below is an example of a basic Workflow Definition.

View the source code

in the context of the rest of the application code.

package yourapp

import (
"time"

"go.temporal.io/sdk/workflow"
)
// ...

// YourSimpleWorkflowDefinition is the most basic Workflow Definition.
func YourSimpleWorkflowDefinition(ctx workflow.Context) error {
// ...
return nil
}

How to define Workflow parameters

Temporal Workflows may have any number of custom parameters. However, we strongly recommend that objects are used as parameters, so that the object's individual fields may be altered without breaking the signature of the Workflow. All Workflow Definition parameters must be serializable.

The first parameter of a Go-based Workflow Definition must be of the workflow.Context type. It is used by the Temporal Go SDK to pass around Workflow Execution context, and virtually all the Go SDK APIs that are callable from the Workflow require it. It is acquired from the go.temporal.io/sdk/workflow package.

The workflow.Context entity operates similarly to the standard context.Context entity provided by Go. The only difference between workflow.Context and context.Context is that the Done() function, provided by workflow.Context, returns workflow.Channel instead of the standard Go chan.

Additional parameters can be passed to the Workflow when it is invoked. A Workflow Definition may support multiple custom parameters, or none. These parameters can be regular type variables or safe pointers. However, the best practice is to pass a single parameter that is of a struct type, so there can be some backward compatibility if new parameters are added.

All Workflow Definition parameters must be serializable and can't be channels, functions, variadic, or unsafe pointers.

View the source code

in the context of the rest of the application code.

package yourapp

import (
"time"

"go.temporal.io/sdk/workflow"
)

// YourWorkflowParam is the object passed to the Workflow.
type YourWorkflowParam struct {
WorkflowParamX string
WorkflowParamY int
}
// ...
// YourWorkflowDefinition is your custom Workflow Definition.
func YourWorkflowDefinition(ctx workflow.Context, param YourWorkflowParam) (*YourWorkflowResultObject, error) {
// ...
}

How to define Workflow return parameters

Workflow return values must also be serializable. Returning results, returning errors, or throwing exceptions is fairly idiomatic in each language that is supported. However, Temporal APIs that must be used to get the result of a Workflow Execution will only ever receive one of either the result or the error.

A Go-based Workflow Definition can return either just an error or a customValue, error combination. Again, the best practice here is to use a struct type to hold all custom values. A Workflow Definition written in Go can return both a custom value and an error. However, it's not possible to receive both a custom value and an error in the calling process, as is normal in Go. The caller will receive either one or the other. Returning a non-nil error from a Workflow indicates that an error was encountered during its execution and the Workflow Execution should be terminated, and any custom return values will be ignored by the system.

View the source code

in the context of the rest of the application code.

package yourapp

import (
"time"

"go.temporal.io/sdk/workflow"
)
// ...

// YourWorkflowResultObject is the object returned by the Workflow.
type YourWorkflowResultObject struct {
WFResultFieldX string
WFResultFieldY int
}
// ...
// YourWorkflowDefinition is your custom Workflow Definition.
func YourWorkflowDefinition(ctx workflow.Context, param YourWorkflowParam) (*YourWorkflowResultObject, error) {
// ...
if err != nil {
return nil, err
}
// Make the results of the Workflow Execution available.
workflowResult := &YourWorkflowResultObject{
WFResultFieldX: activityResult.ResultFieldX,
WFResultFieldY: activityResult.ResultFieldY,
}
return workflowResult, nil
}

How to customize Workflow Type in Go

In Go, by default, the Workflow Type name is the same as the function name.

To customize the Workflow Type, set the Name parameter with RegisterOptions when registering your Workflow with a Worker.

View the source code

in the context of the rest of the application code.

package main

import (
"log"

"go.temporal.io/sdk/activity"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"
"go.temporal.io/sdk/workflow"

"documentation-samples-go/yourapp"
)
// ...
func main() {
// ...
yourWorker := worker.New(temporalClient, "your-custom-task-queue-name", worker.Options{})
// ...
// Use RegisterOptions to set the name of the Workflow Type for example.
registerWFOptions := workflow.RegisterOptions{
Name: "JustAnotherWorkflow",
}
yourWorker.RegisterWorkflowWithOptions(yourapp.YourSimpleWorkflowDefinition, registerWFOptions)
// ...
}

How to develop Workflow logic

Workflow logic is constrained by deterministic execution requirements. Therefore, each language is limited to the use of certain idiomatic techniques. However, each Temporal SDK provides a set of APIs that can be used inside your Workflow to interact with external (to the Workflow) application code.

In Go, Workflow Definition code cannot directly do the following:

  • Iterate over maps using range, because with range the order of the map's iteration is randomized. Instead you can collect the keys of the map, sort them, and then iterate over the sorted keys to access the map. This technique provides deterministic results. You can also use a Side Effect or an Activity to process the map instead.
  • Call an external API, conduct a file I/O operation, talk to another service, etc. (Use an Activity for these.)

The Temporal Go SDK has APIs to handle equivalent Go constructs:

  • workflow.Now() This is a replacement for time.Now().
  • workflow.Sleep() This is a replacement for time.Sleep().
  • workflow.GetLogger() This ensures that the provided logger does not duplicate logs during a replay.
  • workflow.Go() This is a replacement for the go statement.
  • workflow.Channel This is a replacement for the native chan type. Temporal provides support for both buffered and unbuffered channels.
  • workflow.Selector This is a replacement for the select statement. Learn more on the Go SDK Selectors page.
  • workflow.Context This is a replacement for context.Context. See Tracing for more information about context propagation.