Click or drag to resize

Basic workflow

.NET Cadence workflows are defined by an interface and then implemented as a class. We'll start out with a simple HelloWorld console application targeting .NET Core or .NET Framework and adding references to the Neon.Cadence and Neon.Common nuget packages.

C#
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using Neon.Cadence;
using Neon.Common;

namespace HelloWorld
{
    [WorkflowInterface(TaskList = "my-tasks")]
    public interface IHelloWorkflow : IWorkflow
    {
        [WorkflowMethod]
        Task<string> HelloAsync(string name);
    }

    public class HelloWorkflow : WorkflowBase, IHelloWorkflow
    {
        public async Task<string> HelloAsync(string name)
        {
            return await Task.FromResult($"Hello {name}!");
        }
    }

    public static class Program
    {
        public static async Task Main(string[] args)
        {  
            // Connect to Cadence

            var settings = new CadenceSettings()
            {
                DefaultDomain = "my-domain",
                CreateDomain  = true,
                Servers       = new List<string>() { "cadence://localhost:7933" }
            };

            using (var client = await CadenceClient.ConnectAsync(settings))
            {
                // Register your workflow implementation to let Cadence
                // know we're open for business.

                await client.RegisterWorkflowAsync<HelloWorkflow>();
                await client.StartWorkerAsync("my-tasks");

                // Invoke your workflow.

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

                Console.WriteLine(await stub.HelloAsync("Jeff"));
            }
        }
    }
}

Here we define IHelloWorkflow which defines the HelloAsync workflow method which implements your workflow. Workflow interfaces have a couple requirements:

  • Workflow interfaces need to inherit IWorkflow

  • All workflow interface methods must be async

The WorkflowInterfaceAttribute is optional but can be used to specify workflow related options. In this example, we're indicating that the workflow will be registered with Cadence in the my-tasks task queue (we'll be discussing task queues in depth later). All workflow implementations must inherit WorkflowBase and also implement the workflow interface you defined.

Next, we define the HelloWorkflowclass that actually implements the workflow. In this case, the HelloAsync() workflow method simply returns a string value. We tag the method with a WorkflowMethodAttribute to indicate that it's a workflow entry point.

The example above is coded as a basic console application with the Main() method:

  1. Initializes the connection settings to connect Cadence running on the local machine, configuring my-domain as the default domain where your workflows and activities will be registered and run, and indicating that the default domain should be created when it doesn't already exist (handy for testing).

  2. Establishes a connection to Cadence.

  3. Registers the workflow implementation.

  4. Starts the workflow worker. This informs Cadence that you've finished registering workflow and activity implementations and your workflow service is ready to be assigned tasks by Cadence within the my-tasks task queue.

  5. Creates a type-safe stub that implements the specified workflow interface such that calling the method actually executes the workflow. The program then calls the stub's HelloAsync() method and prints the result to the console.

Running a workflow really couldn't be much simpler. Here's what's happening under the covers:

  1. The stub HelloAsync() method tells the Cadence server to start the workflow, with any arguments passed. Note that the stub method doesn't return until the the workflow completes.

  2. Cadence receives this requests and persists the details to Cassandra for reliability and then looks for a workflow service that is registered for my-tasks.

  3. Cadence will see that our sample application has registered itself and will schedule the workflow to be executed by our application.

  4. The application's CadenceClient receives the workflow execution request from Cadence, instantiates an instance of the HelloWorkflow workflow class and then calls the HelloAsync() method.

  5. HelloAsync() does its thing and returns the result string.

  6. Cadence persists the result to Cassandra, marks the workflow as complete, and then signals the pending stub HelloAsync() call that it can return with the value returned by your workflow implementation.

This may not look like much, but Cadence makes this operation inheritly reliable by:

  • Assigning the workflow to an available workflow instance, waiting for one to be ready.

  • Reassigning the workflow if the workflow service executing it is restarted or fails.

  • Ensuring that the workflow is idempotent such that steps that have already been completed won't be reexecuted due to worker failures.

  • Load balancing work across multiple registered workflow service instances.

  • Recording the history of the workflow execution along with the final result.

This example is super simple but even here, Cadence delivers substantial benefits. This would be non-trivial to do reliably with custom code without Cadence.

Note Note

This example is a bit contrived because we are implementing and invoking a workflow in the same program. Normally, folks will deploy a workflow service that runs until terminated and have workflows invoked from other applications such as console or ASP.NET applications. Note that any 64-bit Windows, Linux or OS/X .NET Core or Windows .NET Framework application can use CadenceClient to establish a connection to a Cadence cluster and invoke or manage workflows.

See Also

Reference