Skip to content

Commit d121d14

Browse files
author
Nilay Vishwakarma
committed
base docs
1 parent 80378fd commit d121d14

File tree

8 files changed

+465
-308
lines changed

8 files changed

+465
-308
lines changed

docs/1.Home.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# What is SharpFsm?
2+
3+
A flexible finite state machine (FSM) library for .NET written in C#, it is designed to create and manage Finite State Machines (FSMs) in a simple and efficient way. It allows developers to define states, transitions, and events, enabling the modeling of complex behaviors in a structured manner.
4+
5+
# Features
6+
- **State Management**: Easily define and manage states in your FSM. Strongly-typed FSMs using enums for states
7+
- **Transition Handling**: Transition conditions and side effects.
8+
- **Serialization**: Serialization to/from JSON and YAML.
9+
- **Builder Pattern**: Builder pattern for easy FSM construction.
10+
- **Extensible**: Easily extend the library to fit specific needs or integrate with other systems.
11+
- **Cross-Platform**: Multi-targeted for broad .NET compatibility
12+
13+
14+
# Advantage of using FSM
15+
16+
Using a finite state machine (FSM) for managing state offers several advantages over ad-hoc approaches (like scattered conditionals, flags, or event-driven code) and even over some object-oriented state patterns. Here are the key benefits:
17+
18+
1. Clarity and Explicitness
19+
- All possible states and transitions are explicitly defined.
20+
- The system’s behavior is easy to visualize, reason about, and document.
21+
- Reduces ambiguity and hidden state changes.
22+
2. Predictability and Robustness
23+
- Transitions are controlled and validated.
24+
- Only allowed transitions can occur, preventing invalid or unexpected state changes.
25+
- Makes it easier to handle edge cases and errors.
26+
3. Maintainability
27+
- Adding or modifying states and transitions is straightforward.
28+
- Changes are localized to the FSM definition, not scattered across the codebase.
29+
- Reduces the risk of introducing bugs when requirements change.
30+
4. Testability
31+
- FSMs are easy to test.
32+
- You can systematically test all states and transitions.
33+
- Makes it easier to write unit tests for state-dependent logic.
34+
5. Separation of Concerns
35+
- State logic is separated from business logic.
36+
- Conditions and side effects are encapsulated, making the codebase cleaner and more modular.
37+
6. Scalability
38+
- FSMs scale well as complexity grows.
39+
- Adding new states or transitions does not exponentially increase code complexity, unlike nested if/else or switch statements.
40+
7. Visualization and Documentation
41+
- FSMs can be visualized as state diagrams.
42+
- This aids in communication with stakeholders and helps onboard new developers.
43+
44+
| Approach | Pros of FSM over this approach |
45+
|------------------|------------------------------------------------------|
46+
| If/else, switch | Avoids spaghetti code, centralizes state logic |
47+
| Flags/booleans | Prevents invalid state combinations |
48+
| Event-driven | Makes allowed transitions explicit and predictable |
49+
| State pattern | FSM is more declarative and easier to visualize |
50+
51+
# Use Cases
52+
Here are some common use cases for implementing a finite state machine (FSM) in software development:
53+
54+
1. Workflow and Process Management
55+
- Example: Ticketing systems, order processing, approval workflows.
56+
- Why: Each item moves through a series of well-defined states (e.g., Open → In Progress → Resolved).
57+
2. User Interface (UI) Navigation
58+
- Example: Wizard dialogs, multi-step forms, menu navigation.
59+
- Why: UI components often have distinct states and transitions based on user actions.
60+
3. Game Development
61+
- Example: Character states (Idle, Walking, Jumping, Attacking), enemy AI behaviors.
62+
- Why: Game entities often have clear, rule-based state transitions.
63+
4. Protocol and Communication Handling
64+
- Example: Network protocol implementations (TCP handshake, HTTP request/response), parsers.
65+
- Why: Protocols are defined by sequences of states and transitions based on received data.
66+
5. Device and Hardware Control
67+
- Example: Embedded systems, robotics, IoT devices (e.g., a washing machine: Idle → Washing → Rinsing → Spinning → Done).
68+
- Why: Devices operate in modes with strict rules for moving between them.
69+
6. Authentication and Authorization Flows
70+
- Example: Login processes, multi-factor authentication, session management.
71+
- Why: Security flows require strict control over allowed transitions.
72+
7. Error Handling and Recovery
73+
- Example: Retry logic, circuit breakers, transaction management.
74+
- Why: Systems need to move between normal, error, and recovery states in a controlled way.
75+
8. Text Parsing and Lexical Analysis
76+
- Example: Tokenizers, interpreters, compilers.
77+
- Why: Parsing often involves moving through states based on input characters or tokens.

docs/2.Concepts.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
Understanding the core building blocks of SharpFsm will help you design cleaner, maintainable, and more expressive state machines.
2+
3+
# 📌 State
4+
A state represents a distinct condition or situation in the lifecycle of an object or process.
5+
6+
## How to Define States
7+
States are typically defined using an `enum` for strong typing:
8+
```csharp
9+
public enum TicketState
10+
{
11+
New,
12+
InProgress,
13+
Resolved,
14+
Closed
15+
}
16+
```
17+
## Best Practices
18+
- Use clear and descriptive names (e.g., `PendingReview` > `Pending`).
19+
- Keep the enum definition in a shared or domain-specific namespace.
20+
- Avoid ambiguous or overloaded terms.
21+
22+
# 🔁 Transition
23+
A transition defines how the FSM moves from one state to another.
24+
Each transition can specify:
25+
- Source state: where the transition starts
26+
- Target state: where it ends
27+
- Condition (optional): a predicate to allow or deny the transition
28+
- Side effect (optional): an action to run during the transition
29+
30+
## Example
31+
```csharp
32+
.AddTransition(TicketState.New, TicketState.InProgress)
33+
.When(ctx => ctx.IsAgentAssigned)
34+
.WithSideEffect(ctx => Console.WriteLine("Notified agent"))
35+
.Done()
36+
```
37+
## Condition
38+
A condition (or guard) is a predicate function used to determine if a transition should proceed.
39+
```csharp
40+
ctx => ctx.IsAgentAssigned
41+
```
42+
> Return `true` to allow the transition, `false` to block it.
43+
44+
## Side Effect
45+
A side effect is a piece of logic executed during a transition. Use it for logging, notifications, or state synchronization.
46+
```csharp
47+
(ctx) => Console.WriteLine("Customer notified")
48+
```
49+
50+
# 📦 Context
51+
The context is an object passed into transitions that contains runtime data.
52+
53+
## Example
54+
```csharp
55+
public class TicketContext
56+
{
57+
public bool IsAgentAssigned { get; set; }
58+
public string CustomerEmail { get; set; }
59+
}
60+
```
61+
## Usage
62+
```csharp
63+
AddTransition(TicketState.New, TicketState.InProgress)
64+
.When(ctx => ctx.IsAgentAssigned)
65+
.WithSideEffect((ctx) => Notify(ctx.CustomerEmail))
66+
```
67+
68+
# 🧬 State Machine Definition
69+
A state machine definition encapsulates:
70+
- All valid states
71+
- All transitions between those states
72+
- The initial state
73+
- Any associated logic (guards, side effects, outputs)
74+
75+
This definition is used to construct a state machine instance via a builder.
76+
```csharp
77+
var context = new TicketContext { IsAgentAssigned = false, CustomerEmail = "[email protected]" };
78+
var definition = FiniteStateMachineBuilder<TicketState, TicketContext>.Create("Ticket")
79+
.WithInitialState(TicketState.New)
80+
.AddTransition(TicketState.New, TicketState.InProgress)
81+
.When(ctx => ctx.IsAgentAssigned)
82+
.WithSideEffect((ctx) => Notify(ctx.CustomerEmail))
83+
.Done()
84+
.Build();
85+
var fsm = new FiniteStateMachine<TicketState, TicketContext>(definition);
86+
fsm.TryTransitionTo(TicketState.InProgress, context); // Does not move to InProgress
87+
context.IsAgentAssigned = true;
88+
fsm.TryTransitionTo(TicketState.InProgress, context); // Moves to InProgress
89+
```

docs/3.Getting-Started.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# 📆 Installation
2+
[![NuGet version (SharpWiki)](https://img.shields.io/nuget/v/SharpFsm?label=SharpFsm&logo=nuget)](https://www.nuget.org/packages/SharpFsm/)
3+
4+
> Supported .NET versions: .NET 6, .NET 8, .NET Standard 2.0, 2.1.
5+
6+
```bash
7+
# .NET CLI
8+
dotnet add package SharpFsm
9+
10+
# NuGet Package Manager
11+
Install-Package SharpFsm
12+
13+
# Packet CLI
14+
paket add SharpFsm
15+
```
16+
17+
# 🛠️ Basic Example
18+
Here's a minimal example of a switch with `On` and `Off` states:
19+
```csharp
20+
using SharpFsm;
21+
22+
public enum SwitchState { Off, On }
23+
public class SwitchContext { }
24+
25+
var builder = FiniteStateMachineBuilder<SwitchState, SwitchContext>.Create("Switch")
26+
.WithInitialState(SwitchState.Off)
27+
.AddTransition(SwitchState.Off, SwitchState.On).Done()
28+
.AddTransition(SwitchState.On, SwitchState.Off).Done();
29+
var definition = builder.Build();
30+
var fsm = new FiniteStateMachine<SwitchState, SwitchContext>(definition);
31+
var context = new SwitchContext();
32+
33+
Console.WriteLine(fsm.Current); // Output: Off
34+
fsm.TryTransitionTo(SwitchState.On, context);
35+
Console.WriteLine(fsm.Current); // Output: On
36+
```
37+
38+
# 🧠 Adding Transition Conditions
39+
Transition guards allow you to conditionally permit or deny a state change based on runtime logic. This transition will only succeed during working hours (9 AM to 5 PM).
40+
```csharp
41+
.AddTransition(SwitchState.Off, SwitchState.On)
42+
.When(ctx => DateTime.Now.Hour >= 9 && DateTime.Now.Hour <= 17)
43+
```
44+
45+
> You can write complex logic inside your transition logic like connecting from database, making API calls etc.
46+
47+
# ⚡ Adding Side Effects (On Transition)
48+
You can attach side effects to transitions — useful for logging, triggering events, or updating services.
49+
```csharp
50+
.AddTransition(SwitchState.On, SwitchState.Off)
51+
.WithSideEffect((ctx) =>
52+
{
53+
// Your code goes here
54+
})
55+
``` 

docs/4.Advanced-Usage.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# 🗂️ Transition Registry
2+
3+
`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.
4+
5+
## Key Features
6+
- Conditions: Functions that determine if a transition is allowed, based on the current context.
7+
- Side Effects: Actions to execute when a transition occurs.
8+
- Named Registry: Both conditions and side effects are registered with string keys, so you can reference them by name in your FSM builder.
9+
10+
## Usage
11+
### Register conditions and side effects:
12+
```csharp
13+
var registry = new TransitionRegistry<OrderContext>();
14+
registry.RegisterCondition("PaymentReceived", ctx => ctx.PaymentReceived);
15+
registry.RegisterSideEffect("NotifyShipment", ctx => Console.WriteLine("Order shipped!"));
16+
```
17+
### Reference them by name in your FSM builder:
18+
```csharp
19+
var builder = FiniteStateMachineBuilder<OrderState, OrderContext>.Create("Order")
20+
.WithRegistry(registry)
21+
.AddTransition(OrderState.Created, OrderState.Paid)
22+
.When("PaymentReceived") // Uses the named condition
23+
.WithSideEffect("NotifyShipment") // Uses the named side effect
24+
.Done();
25+
```
26+
## Benefits
27+
- Avoids repetition of logic
28+
- Encourages separation of transition rules from FSM structure
29+
- Easier to test, debug, and document named logic elements
30+
31+
## Notes
32+
- Registry keys must be unique.
33+
- Make sure to register conditions and side effects before using them in the FSM builder.
34+
- You can define multiple registries per context type if needed for modularity.
35+
36+
# 🧾 Serialization
37+
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.
38+
39+
> The `TransitionRegistry` plays a crucial role in serialization by decoupling logic from structure, allowing reusable and maintainable FSMs.
40+
41+
## How Serialization Works
42+
### FSM Definition Serialization
43+
- FSM definitions (states, transitions, initial state) are converted into a serializable DTO: `SerializableStateMachine`.
44+
- Conditions and side effects are not serialized as code — only their names are stored.
45+
- The actual logic is managed externally in the `TransitionRegistry`.
46+
47+
## FSM Definition Deserialization
48+
- Deserialization reconstructs the FSM from its DTO using the FSM builder.
49+
- The builder resolves the named conditions and side effects using a `TransitionRegistry`.
50+
51+
This approach ensures the FSM logic remains modular, shareable, and easy to update.
52+
53+
## Example: JSON Serialization
54+
```csharp
55+
using System.Text.Json;
56+
57+
var builder = FiniteStateMachineBuilder<OrderState, OrderContext>.Create("Order")
58+
.WithInitialState(OrderState.Created)
59+
.WithRegistry(registry)
60+
// ... add transitions ...
61+
;
62+
// Serialize
63+
var serializable = builder.ToSerializable();
64+
string json = JsonSerializer.Serialize(serializable);
65+
// Deserialize
66+
var dto = JsonSerializer.Deserialize<SerializableStateMachine>(json);
67+
var loadedBuilder = FiniteStateMachineBuilder<OrderState, OrderContext>
68+
.Create(dto.EntityType)
69+
.LoadFrom(dto, registry);
70+
var fsm = new FiniteStateMachine<OrderState, OrderContext>(loadedBuilder.Build());
71+
```
72+
73+
## Example: YAML Serialization
74+
```csharp
75+
using YamlDotNet.Serialization;
76+
using YamlDotNet.Serialization.NamingConventions;
77+
78+
// Serialize
79+
var yamlSerializer = new SerializerBuilder()
80+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
81+
.Build();
82+
string yaml = yamlSerializer.Serialize(serializable);
83+
84+
// Deserialize
85+
var yamlDeserializer = new DeserializerBuilder()
86+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
87+
.IgnoreUnmatchedProperties()
88+
.Build();
89+
var dto = yamlDeserializer.Deserialize<SerializableStateMachine>(new StringReader(yaml));
90+
91+
var loadedBuilder = FiniteStateMachineBuilder<OrderState, OrderContext>
92+
.Create(dto.EntityType)
93+
.LoadFrom(dto, registry);
94+
95+
var fsm = new FiniteStateMachine<OrderState, OrderContext>(loadedBuilder.Build());
96+
```
97+
98+
## Why TransitionRegistry is Key
99+
100+
- 🧹 Separation of Structure and Logic: FSM structure is serialized; logic remains in the registry.
101+
- 🔁 Reusability: Named logic can be reused across multiple FSMs.
102+
- 🚚 Portability: Serialized FSMs don’t include executable code, making them environment-agnostic.
103+
- 🛠 Maintainability: Update logic in the registry without touching serialized FSM definitions.
104+

0 commit comments

Comments
 (0)