Click or drag to resize

Cross-platform

Overview

The Cadence platform supports writing and deploying workflows and activities in multiple languages such as GOLANG, Java, and C#/.NET and it is possible for code written in one language to call workflows and activities written in another.

Although we'd love it if you commited to using the .NET Cadence client for all your workflow needs, the time may come where your .NET code needs to interoperate with workflows and activities written in Java or GOLANG. The reverse can also happen, where Java or GOLANG code needs to interoperate with .NET workflows and activities. To accmplish this, you'll need to know some details aboput how Cadence works.

There are two important considerations you'll need to address before making cross-platform calls: identifying the target workflow or activity being called and ensuring that serialized data is compatible for both platforms. We'll discuss each of these below.

Workflow and Activity Type Names

Cadence has a concept called a workflow type name. This is a string used to identify a workflow implementation. For the GOLANG client, workflow type names are generally a combination of the module and name of the function implementing the workflow; like main.MyWorkflow where main is the module name and MyWorkflow is the workfloy function. Custom custom names may be also be specified. The Java and .NET clients are a bit more complex and allow a workflow type to define multiple workflow entry point methods. For these clients, the workflow type name will be a combination of the workflow class and method names (and these may also be customized).

The first step towards cross-platform integration is to determine what the workflow or activity type name is for the target entity. The easiest way to determine this is to go into your Cadence Web UI to discover the actual name used.

Data Serialization

To be able to make cross-platform workflow and activity calls, you'll also need to ensure that both sides are serializing data the same way. There are two parts to this: using the same serialization format on both sides and ensuring that individual items are serialized the same.

Serialization Format: You need to ensure that both platforms are using the same basic mechanism to serialize arguments and results into bytes that can be transmitted on the wire and persisted to the Cadence database. By default, the GOLANG, Java, and .NET clients serialize data to JSON text and then render that into bytes as UTF-8. It is possible to override this by implementing a custom IDataConverter that serializes data as XML, Protobuf, or whatever.

Serialization Details: You also need to ensure that things like JSON property names and details like how date/time and other values are consistent on both sides. The .NET client's default JSON serialization implementation is based on the very popular Newtonsoft JSON.NET package. You can use attributes like [JsonProperty], [JsonIgnore], and [JsonConverter] for fine control over this. Here's a link to more information: JSON.NET Attributes

Cross-platform workflows

There are two approaches to interoperating with workflows written in another language: using an untyped or typed workflow stub.

For untyped workflow stubs, you'll need to connect a CadenceClient to the Cadence cluster, create an untyped WorkflowStub using a WorkflowOptions or ChildWorkflowOptions to specify the exact workflow type name registered for the workflow. Then you'll start the workflow taking care to pass the expected number and types of arguments. You can also use this stub to query and signal the workflow as well as waiting for workflow to complete, obtaining the result.

Note Note

You can also create an untyped WorkflowStub to connect to and manage an existing, already running workflow by workflow ID.

Here's a code snippit demonstrating how to invoke a GOLANG workflow that sends an email. The workflow accepts three string arguments like: bool SendEmail(string to, string subject, string body) and returns true on success. The workflow type name registered SendEmail in the Acme-PROD domain by the GOLANG implementation as "SendEmail":

C#
var settings = new CadenceSettings()
{
    // This specifies the default domain for operations initiated by the
    // client connected below (this can be overridden for specific
    // operations.

    DefaultDomain = "Acme-PROD",

    // Host/port for at least one of the Cadence cluster servers:

    Servers = new List<string>() { "cadence://localhost:7933" }
};

using (var client = await CadenceClient.ConnectAsync(settings))
{
    // Create an untyped stub for the remote workflow.

    var stub = client.NewUntypedWorkflowStub("SendEmail");

    // Start the workflow.  Note that we need to take to ensure that the number, order
    // and types of the parameters match what the GOLANG workflow implementation expects.
    // Untyped workflow stub arguments cannot be checked be the C# compiler.
    // 
    // This method returns a [WorkflowExecution] which includes the workflow and
    // run IDs.  We're not using these here, but real applications may want to
    // persist this so that could check on long-running workflows later.

    var execution = await stub.StartAsync("jeff@lilltek.com", "Test subject", "This is a test email.");

    // Wait for the workflow to complete and return it's result.  Note that we need
    // to explicitly specify the result [bool] type as a generic type parameter.
    // You need to ensure that this matches the workflow implementation as well.

    var success = await stub.GetResultAsync<bool>();

    if (success)
    {
        Console.WriteLine("Email SENT!");
    }
    else
    {
        Console.WriteLine("Email FAILED!");
    }
}

For typed workflow stubs, you'll need to define a workflow interface with methods with argument and result types that match the workflow methods written using another language. In general, the default workflow type name generated by the .NET client will not match the workflow type name registered for workflows written in other languages. So the trick is to decorate your workflow interface methods with [WorkflowMethod(Name = "workflow type name", IsFullName=true] attributes that specify the exact workflow type name registered by the target worker for the workflow.

The IsFullName = true property tells the .NET client that it should target the workflow type name exactly as specified rather than prefixing it with the fully qualified interface name as it would normally do be default. Here's a sample with an interface definition that does this.

C#
[WorkflowInterface]
public interface IMailer : IWorkflow
{
    // Notice that [IsFullName = true] below specifies that "SendEmail"
    // will be used as the target workflow type name without adding the
    // usual prefix.

    [WorkflowMethod(Name = "SendEmail", IsFullName = true)]
    Task<bool> SendEmail(string recipient, string subject, string body);
}

public static async Task Main(params string[] args)
{
    var settings = new CadenceSettings()
    {
        // This specifies the default domain for operations initiated by the
        // client connected below (this can be overridden for specific
        // operations.

        DefaultDomain = "Acme-PROD",

        // Host/port for at least one of the Cadence cluster servers:

        Servers = new List<string>() { "cadence://localhost:7933" }
    };

    using (var client = await CadenceClient.ConnectAsync(settings))
    {
        // Create a typed stub for the remote workflow.

        var stub = client.NewWorkflowStub<IMailer>();

        // Execute the workflow.

        var success = await stub.SendEmail("jeff@lilltek.com", "Test subject", "This is a test email.");

        if (success)
        {
            Console.WriteLine("Email SENT!");
        }
        else
        {
            Console.WriteLine("Email FAILED!");
        }
    }
}
Cross-platform activities

Making cross-platform activity calls work very much like cross-platform workflows and you may use typed or untyped stubs just like we did showed for mworkflows above.

See Also

Reference