Skip to content

Nexai-net/democrite

Repository files navigation

Democrite

GitHub Tag

Nuget Democrite MIT License Help Wanted Good First Issues

Democrite is an open-source framework for building robust, scalable and distributed 'multi-agent like' system based on Microsoft Orleans.

Framework beta level

Caution

The development is still in beta phase

Democrite offers an automated orchestration and configuration system, enabling dynamic creation, editing, and modification of grain interactions.

It incorporates built-in Features to manage virtual grains and facilitate communication between them. This simplifies the creation of various Sequences of virtual grains that can be transformed an input to output. Democrite utilizes Signals to transmit information and Triggers to initiate different Sequences, thereby chaining processes through a graph. Most Features are describe in a serializable structure.

Democrite functions as a layer on top of Microsoft Orleans, ensuring that all features of Orleans are retained.

  • Scalability using silo as virtually one server
  • Robustes through virtual actor model
  • Simplicity by grain design

All configurations possible with Orleans are still available, but Democrite offers a simplified, fluent configuration model.

Release Notes
Teams

Features

  • Nodes : Backend part where all the VGrain live to solve requests.
  • Client : Proxy used to send query to cluter's node.
  • Cluster : Group of client and nodes to distribute work allong multiple devices.
  • Storages : Define where and how the data are persisted.
  • Virtual Grains (vgrain) : Democrite extention of orleans grain.
  • Virtual Grains Id : Democrite dynamic usage of id and templating.
  • Sequences : Linear transformer that chain VGrain to treat a input data and provide an output from it.
  • Signals : Information send through the cluster that could be used as stimulus. (Trigger, Services ...)
  • Triggers : Connectors designed to initiate reactions based on a stimulus. (Signal, Door, Cron, Stream Queue, ...)
  • Doors : Connectors designed to initiate reactions based on stimulus that follow specific conditions.
  • External Code VGrain : VGrain write in other technologie than .net, like Python, C++, Javascript, ...
  • Blackboard : Technologie of shared memory with intelligent controller. Use to organize and process a specific task, goal.
  • Redirections : Enable switch of implementations for a particular contract during runtime.
  • Dynamic Definitions : Allow definition to be generated and inject at runtime.

Tip

In green this is the lastest feature integrated

Nodes

A node refers to a server within a cluster.
It is recommended to establish a client (server API) along with multiple nodes that process requests.
This approach enables scaling of the processing component while maintaining a simplified facade.

Democrite node has an individual setup.

var node = DemocriteNode.Create((ctx, configBuilder) => configBuilder.AddJsonFile("appsettings.json", false),
                                 cfg =>
                                 {
                                     cfg.WizardConfig()
                                        .ClusterFromConfig()
                                        .ConfigureLogging(c => c.AddConsole());
                                        
                                        ...

                                 });

await using (node)
{
    await node.StartUntilEndAsync();
}

Tip

Next Incomming: IHostBuilder integration to allow configuration on existing application.


Client

A client is a separate program that utilizes the cluster's capabilities.
It requires configuration to locate the cluster and send requests.
It is advisable to set up the client and the cluster's nodes on different machines.

Democrite client could be setup individually or through existing application setup.

Individually

var node = DemocriteClient.Create((ctx, configBuilder) => configBuilder.AddJsonFile("appsettings.json", false),
                                   cfg =>
                                   {
                                         cfg.WizardConfig()
                                            .ClusterFromConfig()
                                            .ConfigureLogging(c => c.AddConsole());
                                        
                                        ...

                                     });

await using (node)
{
    await node.StartUntilEndAsync();
}

Integrated

// Add democrite client
builder.Host.UseDemocriteClient(cfg => { ... });

Cluster

Like Orleans, Democrite is build to work in cluster. Client and nodes communicates to each other to solve request.
This solution provide a strong resilience because if one node falldown automatically his work is relocate to other nodes.

To build a cluster you need a way for each new node to discovert the other and share information like status or workload.
Different strategy exist, Orleans choose the database meeting point.

Local

When you work in local you just need to configure using (NoCluster) :

var node = DemocriteNode.Create(cfg =>
                                {
                                     cfg.WizardConfig()
                                        .NoCluster() // -> Setup the system to only bind to 127.0.0.1 and allow only local client

By default, in opposed of Orleans way, the local configuration allow multiple local nodes to form a cluster.
I you want to prevent multiple nodes you have to add the following configuration:

var node = DemocriteNode.Create(cfg =>
                                {
                                     cfg.WizardConfig()
                                        .NoCluster() // -> Setup the system to only bind to 127.0.0.1 and allow only local client
                                        .AddEndpointOptions(new ClusterNodeEndPointOptions(loopback: true, siloPort:50000))

Mongo

Nuget package : Democrite.Framework.Node.Mongo

You can use mongo db as a Meeting point.

To do so you just have configure :

var node = DemocriteNode.Create(cfg =>
                                {
                                     cfg.WizardConfig()
                                        // Setup mongo db as cluster meeting point
                                        .UseMongoCluster(m => m.ConnectionString("127.0.0.1:27017"))

Same go for the client part

builder.Host.UseDemocriteClient(b =>
                                {
                                    b.WizardConfig()
                                     .UseMongoCluster(m => m.ConnectionString("127.0.0.1:27017"))

Important

Attention by default a democrite node doesn't allow client to connect to it. (Except in NoCluster configuration use to dev)
it's a security to prevent any node to be consumed by client.
To enabled the node to be open to client you have two choose:

  1. Add manually a gateway port : .AddEndpointOptions(new ClusterNodeEndPointOptions(gatewayPort: 4242)) // It will conflict if you have multiple node in local
  2. Let system choose a free port as gateway : .AddEndpointOptions(new ClusterNodeEndPointOptions(autoGatewayPort: true))

See the full usage in sample ExternalStorages/MongoDB


Storages

A Democrite cluster need some information to be stored:

  1. Cluster information (cf. Cluster)
  2. VGrain State
  3. Reminder clock information
  4. Definitions (Sequences, triggers, signal, ...)
  5. Custom Data

When you write a VGrain you can inherite from VGrainBase{TVGrainInterface} or VGrainBase{TSTATE, TVGrainInterface}.
The second one provide a State management for you. It means the state will be load/create when the VGrain is activate and store when it is desactivate.

Tip

In case of crash, the state storage could not be garanty, to prevent this you can call in you code the method "PushStateAsync"
The issue with that is the time consumtion

To store the state you need to provide through the constructor a IPersistentState{TState}.
The good practice is to get this instance from dependency injection with tag.

        public CounterVGrain(ILogger<ICounterVGrain> logger,
                             [PersistentState("Counter")] IPersistentState<CounterState> persistentState) 

In this example we request a storage place to store a CounterState object.
Using the attribute PersistentStateAttribute we customize "How" it will be stored.
First parameter is the Storage name (for example the table or collection) and you can specify a second parameter as the Storage configuration Key.

Tip

You can request by constructor as many storage as you want.
It's an easy way to managed the data storage

Mongo Storage

Nuget package : Democrite.Framework.Node.Mongo

You can use the mongo extension to provide storage for all the components:

  • Cluster information -> cf. Cluster

  • Definitions (Sequences, triggers, signal, ...)

    // Define mongo db as definition source
    .AddMongoDefinitionProvider(o => o.ConnectionString("127.0.0.1:27017"))
  • State/Custom storage (VGrain State, Reminder clock information, Custom Data)
    // Setup mongo db as default storage
    .SetupNodeMemories(m =>
    {
        // Enum StorageTypeEnum provide all sub type
        // You can customize the database name, the connection string ...
        // By default it will reuse the LAST connection string configured
        m.UseMongoStorage(StorageTypeEnum.All); 
    });

See the full usage in sample ExternalStorages/MongoDB


Virtual Grains (vgrain)

In accordance with Orleans terminology, a grain is a virtual actor that can appear in any compatible silo, with its state being restored if necessary.

In Orleans, to invoke a grain, one must request a proxy instance from the IGrainFactory. This proxy seamlessly manages the communication between the caller and the called. This is why a grain consists of an interface and an implementation, allowing the proxy to inherit the interface.

With Democrite, there is no need to explicitly call the grain yourself, it will do it for you based on the configuration.

This is the reason we refer to them as Virtual Grains (VGrain), to denote a behavior that prevent direct call consumption.


Sequences

A Sequence is a series of virtual grains executed sequentially, where the output of one can be used as input for the next VGrain.

The aim is to set up this sequence description just once and save it in a database. To run the sequence, only its unique identifier (Uid) is required.

Important

Currently, only local declarations are supported. Please refer to the Next section for information on future capabilities to load these configurations from a storage system, such as databases.

To configure and test sequences you need to create and register it in the DemocriteNode configuration.

Build definition

var collectorSequence = Sequence.Build()
                                // Ask a web URI in input
                                .RequiredInput<Uri>()

                                // Fetch html page and return it
                                .Use<IHtmlCollectorVGrain>().Call((a, url, ctx) => a.FetchPageAsync(url, ctx)).Return

                                // Configure inspector on specific pair inspect and extract current value
                                .Use<IPriceInspectorVGrain>().Configure(currencyPair)
                                                             .Call((a, page, ctx) => a.SearchValueAsync(page, ctx)).Return

                                // Store the value received into a dedicated statefull grain
                                .Use<ICurrencyPairVGrain>().Configure(currencyPair)
                                                           .Call((a, data, ctx) => a.StoreAsync(data, ctx)).Return
                                .Build();

Register definition

var node = DemocriteNode.Create((ctx, configBuilder) => configBuilder.AddJsonFile("appsettings.json", false),
                                 cfg =>
                                 {
                                     cfg.WizardConfig()
                                        .NoCluster()

                                        .ConfigureLogging(c => c.AddConsole())

                                        .AddInMemoryMongoDefinitionProvider(m =>
                                        {
                                            // Local in node memory setup
                                            .SetupSequences(collectorSequence);
                                        })

Triggers

A Sequences can be executed manually, but it can also be triggered automatically.

There are differents kind of triggers :

  • Time Periodicity, use a cron expression to define the periodicity
  • Signals, trigge when configured signal is also fire

Similar to sequences, trigger definitions can currently be created and stored locally, with plans for future storage in external sources like databases.

Important

Currently, only local declarations are handled. The upcoming goal, detailed in the Next section, is to enable loading these configurations from a storage source.

A trigger can supply an input to initiate the sequence.

Important

Currently, only static data collection is supported. Please see the Next section for information on the future goal of loading these configurations from an external provider.

Time Periodicity

                               // Every minutes between 9h and 18h UTC between monday and friday
var triggerDefinition = Trigger.Cron("* 9-18 * * mon-fri") 
                               
                               // Define what will be trigged (Sequence or signals)
                               .AddTarget(collectorSequence)

                               // You could have many target or many types

                               //.AddTarget(collectorSequence2)
                               //.AddTarget(collectorSequence3)

                               // Define how to get input information that will be send to targets
                               .SetInputSource(input => input.StaticCollection(collectionsources)
                                                             .PullMode(PullModeEnum.Circling)
                                                             .Build())
                               .Build();

Signals

                                    // listen inputSignal and trigger when this one is fire
var signalTriggerDefinition = Trigger.Signal(inputSignal)

                                    // Define what will be trigged
                                    .AddTargetSequence(collectorSequence)

                                    .SetInputSource(input => input.StaticCollection(collectionsources)
                                                                    .PullMode(PullModeEnum.Circling)
                                                                    .Build())
                                    .Build();

Register definition

var node = DemocriteNode.Create((ctx, configBuilder) => configBuilder.AddJsonFile("appsettings.json", false),
                                 cfg =>
                                 {
                                     cfg.WizardConfig()
                                        .NoCluster()

                                        .ConfigureLogging(c => c.AddConsole())

                                        .AddInMemoryMongoDefinitionProvider(m =>
                                        {
                                            // Local in node memory setup
                                            m.SetupTriggers(signalTriggerDefinition);
                                        })

Stream

Use an EBS (Entreprise Bus Service) as storage an diffuseur of job to to.

Orlean can nativaly used different type of ESB.
With Democrite we create connector through trigger to push and pull.

Send to stream :

// PUSH
var trigger = Trigger.Cron("*/25 * * * * *", "TGR: Push Every 25 sec")
                     
                     .AddTargetStream(streamDef) // <----- Add stream as target

                     .SetOutput(s => s.StaticCollection(Enumerable.Range(0, 50))
                                            .PullMode(PullModeEnum.Broadcast))
                     .Build();

Consume from stream :

// PUSH
var fromStreamTrigger = Trigger.Stream(streamDef) // <----- Trigger that fire until theire is a message in the stream queue
                               .AddTargetSequence(consumeSeq.Uid)

                               // Limit the number of concurrent execution, Prevent consuming all messages without resources in the cluster to process them (CPU, RAM, ...)
                               .MaxConcurrentProcess(2)

                               .Build();

Signals

The signals feature consists of two components:

  • Signal
  • Door

A signal functions similarly to an event, but with a "fire and forget" approach.

By default, the signal includes:

  • Definition name & Uid
  • The VGrain information that fire
  • The datetime when it is fire
  • The possible previous signal that cause this one to fire

However, it can carry a small amount of information.
It is recommended to keep this information as minimal as possible to avoid memory issues.
It could be something as simple as an ID referencing data in storage.

Define a signal:

var signalA = Signal.Create("signalA");


A Door can listen to multiple signals and, based on specific conditions, can emit its own signal.

Currently, a boolean logic door is available, but you can easily create and configure your own gate logic.

Define a Logic boolean door:

 var door = Door.Create("CheckPairAboveAverage")
                .Listen(valueEurUsdStoredAboveAverage, valueEurChfStoredAboveAverage)

                // Basic
                // Fire If (A & B) are fired in a 10 sec window
                // By default the door unlock as soon as the condition is valid and 
                // signal activation are only use one.
                .UseLogicalAggregator(LogicEnum.And, TimeSpan.FromSeconds(10))

                // Advanced
                //.UseLogicalAggregator(b =>
                //{
                //    return b.Interval(TimeSpan.FromSeconds(0.5))
                //            .AssignVariableName("A", valueEurUsdStoredAboveAverage)
                //            .AssignVariableName("B", valueEurChfStoredAboveAverage)
                //            .AssignVariableName("C", manualForceDoorFireing)

                //            /* Fire (if A and B are signal in an interval of 0.5 second except if i was already fire in less than 0.5 seconds)
                //                    Or
                //                    C
                //             */
                //            .Formula("(A & B & !this) | C");
                //})
                .Build();

Door Types

  • LogicalAggregator : Apply a boolean condition based on signal activation (1 if activate on period of time, otherwise 0)
  • RelayFilterDoor: Apply a condition a the signal structure itself, use a filter to trigger specific sequence

Register definition

var node = DemocriteNode.Create((ctx, configBuilder) => configBuilder.AddJsonFile("appsettings.json", false),
                                 cfg =>
                                 {
                                     cfg.WizardConfig()
                                        .NoCluster()

                                        .ConfigureLogging(c => c.AddConsole())

                                        .AddInMemoryMongoDefinitionProvider(m =>
                                        {
                                            // Local in node memory setup
                                            m.SetupSignals(t => t.Register(signalA))
                                             .SetupDoors(t => t.Register(door));
                                        })

Virtual Grain Id

In the Orleans framework, a grain definition can have multiple virtual instances.
However, only one instance is active at any given time, associated with a unique identifier known as a GrainId.

In Orleans, it is the user's responsibility to supply the correct GrainId of the grain they wish to call.

In Democrite, virtual grains are instantiated and called by a generic orchestrator.
By default, a new Guid is used each time, which is ideal for stateless grain.

Virutal Grain interface could be tag by attribute VGrainIdFormatAttribute to indicate how to build the GrainId.

The template ID system provides the ability to dynamically create a GrainId using data input or execution context as the source of information.

You can see a good example in the sample Forex.
This one use stateless virtual grain (vgrain) to download html page and parse it
but use a statefull virtual grain (vgrain) to store the value extracted.

This virtual grain (vgrain) employs a string value, such as a forex pair (eur-usd, eur-chf, etc.), from the execution context to form its GrainId, resulting in the creation of a single reusable instance for each pair

This allow :

  • A client to directly call this vgrain to extract the values.
  • To create only one grain by pair that is single-thread handled by orleans (no need to think of concurrent access)
  • Store information in dedicate a serializable model in class and not to focus on the storage mode (databases, files, ...)

Tip

You can access those grain usign the classic orleans IGrainFactory way.
BUT it is better to use IDemocriteExecutionHandler who will use correctly and automatically the correct GrainId.


External Code VGrain

With Democrite we can use other satellite program or script to perform jobs.

To do so you need simple steps:

  • Create Code artifact definitions
  • Using through a vgrain
    • Use generic vgrain in the library IGenericArtifactExecutableVGrain
    • Create your own vgrain by inherite from ArtifactExecutableBaseVGrain<> instead of VGrainBase

Python

A python package exist to handle the communication protocol with democrite README.md
You can found a full sample Here


Blackboard

A blackboard is a temporary shared space with specific controllers to solve a problem.
The goal is to group specific analyze result and have controllers deciding the next step to acheived to a solution.

You can found a full sample Here


Redirections

Democrite has devised a dynamic solution for determining the implementation to be utilized for a contract.
There are several reasons why multiple implementations might be necessary.
Now, with the introduction of a feature called "Redirection" you can dynamically alter the implementation used for a specific contract.

It is usefull for:

  • Testing: Ensure that implementation won't break existing sequences
  • Competition: You can them make implementation in competition to select the best related to context.
  • ...

This redirection could be setup a different level:

  • Call Scope: During a call using IDemocriteExucutorHanlder. Limit the redirection only to a specific call, you can even specialize to a specific stage.
  • Global Scope: Available on all the cluster

You can found a full sample Here


Dynamic Definitions

Democrite serves as an orchestrator for vgrains, utilizing serializable definitions as work orders.
These definitions are primarily formulated and stored in an external system such as databases.

The objective of the "Dynamic Definitions" feature is to generate definitions (sequences, triggers, etc.) at runtime, with a lifecycle linked to storage.
If no specific storage is provided, the definition will exist only for the duration of the cluster's lifetime.

This capability enables dynamic testing of new sequences, creation of temporary triggers, and activation of temporary debugging features, among other functionalities.

You can found a full sample Here

Quick Start

Nuget Packages

Important

For now democrite is in beta version. Don't forget to use the pre-release flag in visual studio

Nuget Democrite

  • Democrite.Framework.Node: Reference this one by your node project.
  • Democrite.Framework.Client: Reference this one by your client project.
  • Democrite.Framework.Builder: Reference this one by your project that build definitions.

If you split the agent implementation and definition in separate projet you could only reference the nuget package Democrite.Framework.Core

Framework Feature

  • Democrite.Framework.Node.Cron: Reference this one by your node project to enable the cron mechanism.
  • Democrite.Framework.Node.Signals: Reference this one by your node project to enable the signals mechanism.
  • Democrite.Framework.Node.StreamQueue: Reference this one by your node project to enable the stream mechanism.
  • Democrite.Framework.Node.Blackboard: Reference this one by your node project to enable the stream mechanism.

Extensions

  • Democrite.Framework.Extensions.Mongo: Reference this one by your node project to enable the mongo db Storage.

Bags

  • Democrite.Framework.Bag.DebugTools: Reference this one by your node project to enable debug sequences or VGrain (Like Display, ...).
  • Democrite.Framework.Bag.Toolbox: Reference this one by your node project to enable basic tools sequences or VGrain (like delay, ...).

Node

To create a node you just have to follow the example bellow.

Caution

Orleans scan by default all the project dll. Due to .net assembly load behavior if you deport your agent implementation in another projet is may not be loaded if you don't directly use any of the type defined. Reference the project is not enough. In the Next section you will see an objectif to reference assembly to load for now you have to use the SetupVGrains method in the wizard configurator.

In Program.cs:

var node = DemocriteNode.Create((ctx, configBuilder) => configBuilder.AddJsonFile("appsettings.json", false),
                                   cfg =>
                                   {
                                         cfg.WizardConfig()
                                            .ClusterFromConfig()
                                            .Configure(b => b.ConfigureLogging(logging => logging.AddConsole()));
                                        
                                        ...

                                     });

await using (node)
{
    await node.StartUntilEndAsync();
}

Client

To create a client you just have to follow the example bellow.

Caution

All nodes and clients need a meeting point to know the others and form a cluster, orleans choose the database strategy. By default only one node and one client could be present on the same machine wihtout any db setup. But You could use the orleans fluent method to configure your cluster and client. You can now use different database solution for all storage type look for #Cluster section

In Program.cs:

var node = DemocriteClient.Create((ctx, configBuilder) => configBuilder.AddJsonFile("appsettings.json", false),
                                   cfg =>
                                   {
                                         cfg.WizardConfig()
                                            .ClusterFromConfig()
                                            .Configure(b => b.ConfigureLogging(logging => logging.AddConsole()));
                                        
                                        ...

                                     });

await using (node)
{
    await node.StartUntilEndAsync();
}

Consume Democrite Cluster

To execute a sequence or call a specific grain you have to use the service IDemocriteExecutionHandler.

This handler follow democrite rules in grain id generation.


Tips

  1. Normalize your data model and create small Virtual Grain with small sponsability.
  2. Follow the SRP (Single Repsonsability Principle) as describe in the S.O.L.I.D pattern
  3. If you attach information to signal use small one like simple id.
  4. Prefer democrite configuration if possible to prevent any side effect non managed

Samples

Forex

In the section Sample/Forex

Use case

Fetch reguraly a forex pair value using public web site, store the values and be able to consume them through an api.

Features Use


External Storage

Use case

Create a node cluster with a client connected through external storage. In storage we will also store definitions of a trigger that will every minute increment a counter tag with a name. Through the client you can look for the counter value through a swagger api

Features Use

Mongo
In the section samples/ExternalStorages/MongoDB


Relay Filter Door

In the section Sample/RelayFilterDoor

Use case

Relay filter door can apply a condition on signal received. For example if the signal transport a value of type int, let pass.

  • Trigger dedicated sequence if function of the data carry
  • Trigger dedicated storage based to the type of data carry

Features Use

  • Democrite RelayFilterDoor definition

Python VGrains

In the section Sample/PythonVGrains

Use case

Use Python scripts inside democrite environment like VGrain standard

Caution

In version 0.2.2 only local python script deployed with the democrite node as 'Content' is supported
You goal is to be able to support different package provider,
in different format late on. (From api, zip, shared folder, resource embedded ...)

  • Use RNN (Neural Network) library in python to solve data
  • Re-use you librairies

Features Use

  • Democrite Artifact Definition, Packaging
  • External code executor (allow democrite to used external program as solver)

Blackboard

In the section Sample/Blackboard

Use case

Use a Blackboard as central point to group information related to a specific case and compute them when needed.
We demonstrate with a simple calculator. We can store in the blackboard different type of numeriacl value and sum them when a limit is reach or manually.

  • Process Different modality (Text, Video, Image) in a same place and use dedicated controller to perform advance analyze, remove useless data, archive some others ...
  • Train an IA by storing good data set and rejet others, detect automatically when quality response is reach

Features Use

  • Blackboard Storage

    • Rules
    • Type check
    • Multi-Storage
  • Blackboard Controller

    • Storage : Responsable of the data integrity
    • Event : Responsable to compute when condition are fullfill.

Streams

In the section Sample/Streams

Use case

Use a Stream as data input for democrite activity through a specific trigger.

  • Buffer a lot of work in a StreamQueue
  • Communicate with external system in and out

Features Use

  • Stream source trigger

Redirections

In the section Sample/Redirections

Use case

  • Wanted to change the behavior of some implementation whitout impacting existing.
  • Be able to put in competition multiple implementation an see the best result

Features Use

  • Grain Implemation redirection
    • Local during a call
    • Global for all the cluster

Dynamic Defintions

In the section Sample/DynamicDefinition

Use case

  • A blackboard need some data processing, it can generate it's own sequence and signal definition at runtime
  • You want to test a VGrain in some condition you can create you sequence using it
  • You pilote IA want to test different algorithm variation

Features Use

  • Dynamic definition
    • Injection
    • Activation/Deactivation
    • Suppression

Next

Static Badge

v 0.4.1-prerelease:
Release Node

  • Migrate to .net 8
  • Force grain redirection for a precise sequence execution
  • Call sequence from sequence
  • Create sequence definition in runtime

Static Badge

v 0.3.0-prerelease:
Release Node

  • Blackboard extensions
  • Create bags as container of generic (toolbox, debug)
  • Repository : global & simple storage system not linked to a grain state
  • Process through a foreach a sub properties collection in a sequence
  • Trigger use to push or pull data from a stream
  • Fire signal from sequence

v 0.2.2-prerelease:
Release Node

  • Fully integrate Python VGrain in Democrite environment

v 0.2.1-prerelease:
Release Note

  • Easy cluster external storage to store virtual grain state, reminder, membership ...
  • IHostBuilder integration to allow configuration on existing application.
  • Load Definition, sequence, signals, triggers, ... from an external source like databases using the design pattern strategy through IProviderSource

Versions

Democrite Version Minimal .net version Minimal orlean version
Lastest .net 8.0 8.0.0
0.3.0 .net 7.0 7.2.4
0.2.1 .net 7.0 7.0.3

References

About

Orchestration framework allowing to execute dynamic grains configurations to create a meta agent cluster only limited by grains implementations.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages