zb-client-accelerator 1.0.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package zb-client-accelerator --version 1.0.1                
NuGet\Install-Package zb-client-accelerator -Version 1.0.1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="zb-client-accelerator" Version="1.0.1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add zb-client-accelerator --version 1.0.1                
#r "nuget: zb-client-accelerator, 1.0.1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install zb-client-accelerator as a Cake Addin
#addin nuget:?package=zb-client-accelerator&version=1.0.1

// Install zb-client-accelerator as a Cake Tool
#tool nuget:?package=zb-client-accelerator&version=1.0.1                

BUILD ANALYZE alternate text is missing from this package README image alternate text is missing from this package README image alternate text is missing from this package README image alternate text is missing from this package README image Compatible with: Camunda Platform 8 alternate text is missing from this package README image

Bootstrap Accelerator for the C# Zeebe client

This project is an extension of the C# Zeebe client project. Zeebe Workers are automatically recognized and bootstrapped via a .Net HostedService.

Read the Zeebe documentation for more information about the Zeebe project.

The basic idea and implementation for this came from https://github.com/camunda-community-hub/zeebe-client-csharp-bootstrap. We loved the idea, but had in some parts our own preferences for defaults, behaviour and separation of concerns. So this is our version of a good Bootstrap Extension for the C# Zeebe Client. Credits for the base work still belong to https://github.com/arjangeertsema.

Requirements

How to use

The Zeebe C# client bootstrap extension is available via nuget (https://www.nuget.org/packages/zb-client-accelerator/).

Recommendation: a complete sample project using this extension can be found in examples.

Quick start

All classes which implement IZeebeWorker, IAsyncZeebeWorker, IZeebeWorkerWithResult or IAsyncZeebeWorkerWithResult are automatically added to the service collection and autowired to Zeebe when you register this bootstrap project with the IServiceCollection.BootstrapZeebe() extension method.

More power is provided by using global::Zeebe.Client.Accelerator.Extensions; which provides you with further extensions for IHost, IZeebeClient etc. in order to deploy processes or create one time message receivers.

Bootstrap Zeebe

The BootstrapZeebe method has two parameters:

  1. ZeebeBootstrapOptions via configuration, action delegate or both.
  2. An array with assemblies which will be scanned for job handlers.
ConfigureServices((hostContext, services) => {
    services.BootstrapZeebe(
        hostContext.Configuration.GetSection("ZeebeConfiguration"),
        this.GetType().Assembly
    );
})

Example Web Application:

// Start building my WebApplication
var builder = WebApplication.CreateBuilder(args);

// Bootstrap Zeebe Integration
builder.Services.BootstrapZeebe(
    builder.Configuration.GetSection("ZeebeConfiguration"),
    typeof(Program).Assembly);

The configuration will e.g. look as follows:

{
  "ZeebeConfiguration": {
    "Client": {
      "GatewayAddress": "127.0.0.1:26500"
    },
    "Worker": {
      "MaxJobsActive": 5,
      "TimeoutInMilliseconds": 500,
      "PollIntervalInMilliseconds": 50,
      "PollingTimeoutInMilliseconds": 1000,
      "RetryTimeoutInMilliseconds": 1000
    }
  },
}

Deploy Processes

If we want to deploy some processes right before the final startup of our application we create a deployment using the extension for IHost or IServiceProvider as follows:

var app = builder.Build();
...
// Deploy all process resources
app.CreateZeebeDeployment()
    .UsingDirectory("Resources")
    .AddResource("insurance_application.bpmn")
    .AddResource("document_request.bpmn")
    .AddResource("risk_check.dmn")
    .Deploy();

// Now run the application
app.Run();

Zeebe Workers

A Zeebe Worker is an implementation of IZeebeWorker, IAsyncZeebeWorker, IZeebeWorkerWithResult or IAsyncZeebeWorkerWithResult. Zeebe Workers are automatically added to the DI container, therefore you can use dependency injection inside. The default worker configuration can be overwritten with AbstractWorkerAttribute implementations, see attributes for more information.

[JobType("doSomeWork")]
public class SomeWorker : IAsyncZeebeWorker
{
    private readonly MyApiService _myApiService;

    public SimpleJobHandler(MyApiService myApiService)
    {
        _myApiService = myApiService;
    }

    /// <summary>
    /// Handles the job "doSomeWork".
    /// </summary>
    /// <param name="job">the Zeebe job</param>
    /// <param name="cancellationToken">cancellation token</param>
    public async Task HandleJob(ZeebeJob job, CancellationToken cancellationToken)
    {  
        // execute business service etc.
        await _myApiService.DoSomethingAsync(cancellationToken);
    }
}

Of course you are able to access process variables and return a result. E.g.:

[JobType("doAwesomeWork")]
public class AwesomeWorker : IAsyncZeebeWorker<SimpleJobPayload, SimpleResponse>
{
    ...

    public async Task<SimpleResponse> HandleJob(ZeebeJob<SimpleJobPayload> job, CancellationToken cancellationToken)
    {  
        // get variables as declared (SimpleJobPayload)
        var variables = job.getVariables();

        // execute business service etc.
        var result = await _myApiService.DoSomethingAsync(variables.CustomerNo, cancellationToken);
        return new SimpleResponse(result);
    }

    class SimpleJobPayload
    {
        public string CustomerNo { get; set; }
    }
}

The above code will fetch exactly the variables defined as attributes in SimpleJobPaylad from the process.

And there are more options, including the option to access custom headers configured in the process model:

[JobType("doComplexWork")]
public class ComplexWorker : IAsyncZeebeWorker
{
    ...

    public async Task HandleJob(ZeebeJob job, CancellationToken cancellationToken)
    {  
        // get all variables (and deserialize to a given type)
        ProcessVariables variables = job.getVariables<ProcessVariables>();
        // get custom headers (and deserialize to a given type)
        MyCustomHeaders headers = job.getCustomHeaders<MyCustomHeaders>();

        // execute business service etc.
        await _myApiService.DoSomethingComplex(variables.Customer, headers.SomeConfiguration, cancellationToken);
        ...
    }

    class ProcessVariables
    {
        public string? BusinessKey { get; set; }

        public CustomerData Customer { get; set; }

        public string? AccountName { get; set; }

        ...
    }

    class MyCustomHeaders
    {
        public string SomeConfiguration { get; set; }
    }
}

The following table gives you an overview of the available options:

Interface Description Fetched Variables
IAsyncZeebeWorker Asynchronous worker without specific input and no response Default is to fetch all process variables. Use FetchVariables attribute for restictions.
IAsyncZeebeWorker<TInput> Asynchronous worker with specific input and no response Fetches exactly the variables defined as attributes in TInput.
IAsyncZeebeWorker<TInput, TResponse> Asynchronous worker with specific input and specific response Fetches exactly the variables defined as attributes in TInput.
IAsyncZeebeWorkerWithResult<TResponse> Asynchronous worker without specific input but a specific response Default is to fetch all process variables. Use FetchVariables attribute for restrictions.
IZeebeWorker Synchronous worker without specific input and no response Default is to fetch all process variables. Use FetchVariables attribute for restictions.
IZeebeWorker<TInput> Synchronous worker with specific input and no response Fetches exactly the variables defined as attributes in TInput.
IZeebeWorker<TInput, TResponse> Synchronous worker with specific input and specific response Fetches exactly the variables defined as attributes in TInput.
IZeebeWorkerWithResult<TResponse> Synchronous worker without specific input but a specific response Default is to fetch all process variables. Use FetchVariables attribute for restrictions.

If you like to explicitely restrict the variables fetched from Zeebe, you have the following additional option:

[JobType("doComplexWork")]
[FetchVariables("businessKey", "applicantName")]
public class SimpleWorker : IAsyncZeebeWorker
{
   ...
}

In case you do not want to fetch any variables at all from Zeebe, use [FetchVariables(none: true)]:

[JobType("doSimpleWork")]
[FetchVariables(none: true)]
class SimpleWorker : IZeebeWorker
{
   ...
}

A handled job has three outcomes:

  1. The job has been handled without exceptions: this will automaticly result in a JobCompletedCommand beeing send to the broker. The optional TResponse is automaticly serialized and added to the JobCompletedCommand.
  2. A BpmnErrorException has been thrown while handling the job: this will automaticly result in a ThrowErrorCommand beeing send to the broker triggering Error Boundary Events in the process.
  3. Any other unexpected exception will automatically result in a FailCommand beeing send to the broker including message details and reducing the number of retries;

Dynamic message receiver

See Example for synchronous responses from processes for a description of the scenario.

You can create a one time job handler for receiving a message for a dynamic job type "received_" + number as follows:

try
{
    string jsonContent = _zeebeClient.ReceiveMessage("received_" + number, TimeSpan.FromSeconds(5), "someVariable1", "someVariable2");
    ...
} catch (MessageTimeoutException)
{
    // nothing received
    ...
}

Of course it is possible to use a typed response, which will automatically fetch and deserialize all variables defined as attributes in the given type:

MyVariables typedContent = _zeebeClient.ReceiveMessage<MyVariables>("received_" + number, TimeSpan.FromSeconds(3));

Simply waiting without receiving any variables:

bool messageReceived = _zeebeClient.ReceiveMessage("received_" + number, TimeSpan.FromSeconds(3));

The one time job handler will be destroyed after ReceiveMessage returns.

Hints

  1. By default the workers are added to de DI container with a Transient service lifetime. This can be overriden by adding the ServiceLifetimeAttribute to the worker, see attributes for more information.
  2. By default the ZeebeVariablesSerializer is registered as the implementation for IZeebeVariablesSerializer which uses System.Text.Json.JsonSerializer. Serialization / Deserialization always uses CamelCase as naming policy!
  3. The default job type of a worker is the class name of the worker. This can be overriden by adding the JobTypeAttribute to the worker, e.g. [JobType("myJobName")].

How to build

Run dotnet build Zeebe.Client.Accelerator.sln

How to test

Run dotnet test Zeebe.Client.Accelerator.sln

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.1.11 3,317 9/30/2024
2.1.10 5,075 7/31/2024
2.1.9 532 7/17/2024
2.1.8 1,896 5/24/2024
2.1.7 1,423 3/27/2024
2.1.6 319 3/15/2024
2.1.5 2,229 3/4/2024
2.1.4 4,831 12/20/2023
2.1.3 5,994 10/16/2023
2.1.2 545 10/15/2023
2.1.1 490 10/15/2023
2.1.0 496 10/15/2023
2.0.0 611 8/18/2023
1.1.0 593 8/18/2023
1.0.4 3,654 5/31/2023
1.0.3 929 5/25/2023
1.0.2 1,869 4/6/2023
1.0.1 979 3/17/2023
1.0.0 785 3/1/2023
0.8.5 829 12/15/2022
0.8.4 795 12/9/2022
0.8.3 739 12/1/2022
0.8.2 832 11/29/2022
0.8.1 859 11/29/2022