4. Advanced Usage - iwannabebot/SharpFsm GitHub Wiki
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.
- 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.
var registry = new TransitionRegistry<OrderState, OrderContext>();
registry.RegisterCondition("PaymentReceived", ctx => ctx.PaymentReceived);
registry.RegisterSideEffect("NotifyShipment", (ctx, _, _) => Console.WriteLine("Order shipped!"));
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();
- Avoids repetition of logic
- Encourages separation of transition rules from FSM structure
- Easier to test, debug, and document named logic elements
- 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 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.
- 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
.
- 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.
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());
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());
- ๐งน 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.