4. Advanced Usage - iwannabebot/SharpFsm GitHub Wiki

๐Ÿ—‚๏ธ Transition Registry

TransitionRegistry<TContext> is a utility class in SharpFsm that allows you to register and reuse named conditions and side effects for your state machine transitions. This makes your FSM definitions cleaner, more maintainable, and enables sharing logic across multiple transitions.

Key Features

  • Conditions: Functions that determine if a transition is allowed, based on the current context.
  • Side Effects: Actions to execute when a transition occurs.
  • Named Registry: Both conditions and side effects are registered with string keys, so you can reference them by name in your FSM builder.

Usage

Register conditions and side effects:

var registry = new TransitionRegistry<OrderState, OrderContext>();
registry.RegisterCondition("PaymentReceived", ctx => ctx.PaymentReceived);
registry.RegisterSideEffect("NotifyShipment", (ctx, _, _) => Console.WriteLine("Order shipped!"));

Reference them by name in your FSM builder:

var builder = FiniteStateMachineBuilder<OrderState, OrderContext>.Create("Order")
    .WithRegistry(registry)
    .AddTransition(OrderState.Created, OrderState.Paid)
        .When("PaymentReceived") // Uses the named condition
        .WithSideEffect("NotifyShipment") // Uses the named side effect
        .Done();

Benefits

  • Avoids repetition of logic
  • Encourages separation of transition rules from FSM structure
  • Easier to test, debug, and document named logic elements

Notes

  • Registry keys must be unique.
  • Make sure to register conditions and side effects before using them in the FSM builder.
  • You can define multiple registries per context type if needed for modularity.

๐Ÿงพ Serialization

Serialization in SharpFsm allows you to save and load your finite state machine (FSM) definitions. This enables persistence, versioning, portability, and sharing of FSMs. It supports multiple formats including JSON and YAML.

The TransitionRegistry plays a crucial role in serialization by decoupling logic from structure, allowing reusable and maintainable FSMs.

How Serialization Works

FSM Definition Serialization

  • FSM definitions (states, transitions, initial state) are converted into a serializable DTO: SerializableStateMachine.
  • Conditions and side effects are not serialized as code โ€” only their names are stored.
  • The actual logic is managed externally in the TransitionRegistry.

FSM Definition Deserialization

  • Deserialization reconstructs the FSM from its DTO using the FSM builder.
  • The builder resolves the named conditions and side effects using a TransitionRegistry.

This approach ensures the FSM logic remains modular, shareable, and easy to update.

Example: JSON Serialization

using System.Text.Json;

var builder = FiniteStateMachineBuilder<OrderState, OrderContext>.Create("Order")
    .WithInitialState(OrderState.Created)
    .WithRegistry(registry)
    // ... add transitions ...
    ;
// Serialize
var serializable = builder.ToSerializable();
string json = JsonSerializer.Serialize(serializable);
// Deserialize
var dto = JsonSerializer.Deserialize<SerializableStateMachine>(json);
var loadedBuilder = FiniteStateMachineBuilder<OrderState, OrderContext>
    .Create(dto.EntityType)
    .LoadFrom(dto, registry);
var fsm = new FiniteStateMachine<OrderState, OrderContext>(loadedBuilder.Build());

Example: YAML Serialization

using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

// Serialize
var yamlSerializer = new SerializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .Build();
string yaml = yamlSerializer.Serialize(serializable);

// Deserialize
var yamlDeserializer = new DeserializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .IgnoreUnmatchedProperties()
    .Build();
var dto = yamlDeserializer.Deserialize<SerializableStateMachine>(new StringReader(yaml));

var loadedBuilder = FiniteStateMachineBuilder<OrderState, OrderContext>
    .Create(dto.EntityType)
    .LoadFrom(dto, registry);

var fsm = new FiniteStateMachine<OrderState, OrderContext>(loadedBuilder.Build());

Why TransitionRegistry is Key

  • ๐Ÿงน Separation of Structure and Logic: FSM structure is serialized; logic remains in the registry.
  • ๐Ÿ” Reusability: Named logic can be reused across multiple FSMs.
  • ๐Ÿšš Portability: Serialized FSMs donโ€™t include executable code, making them environment-agnostic.
  • ๐Ÿ›  Maintainability: Update logic in the registry without touching serialized FSM definitions.
โš ๏ธ **GitHub.com Fallback** โš ๏ธ