FluidBus 1.0.2
dotnet add package FluidBus --version 1.0.2
NuGet\Install-Package FluidBus -Version 1.0.2
<PackageReference Include="FluidBus" Version="1.0.2" />
<PackageVersion Include="FluidBus" Version="1.0.2" />
<PackageReference Include="FluidBus" />
paket add FluidBus --version 1.0.2
#r "nuget: FluidBus, 1.0.2"
#:package FluidBus@1.0.2
#addin nuget:?package=FluidBus&version=1.0.2
#tool nuget:?package=FluidBus&version=1.0.2
FluidBus
A lightweight, modular event bus for .NET 10 built around two dispatch models: Router (protocol-based routing with sync/async support) and React (channel-based reactive subscriptions), plus a CallBack registry and a built-in Benchmark toolkit.
Zero external dependencies.
Architecture
FluidBus
├── FluidBus.Core # Interfaces, protocols, tasks, errors
├── FluidBus.Router # Protocol-based event routing (sync/async)
├── FluidBus.React # Reactive channel-based event dispatch
├── FluidBus.CallBack # Named callback registry
├── FluidBus.Benchmark # Benchmarking utilities
└── FBus # Unified facade
Router vs React
| Router | React | |
|---|---|---|
| Dispatch | Protocol-based (sync/async) | Channel-based (always async) |
| Handler count | One handler per event type | Multiple subscribers per channel |
| Registration | Manual via FRouter.Register() |
Auto-subscribe on instantiation |
| Matching | Exact event type | All subscribers on the channel |
| Use case | Command / request patterns | Broadcast / observer patterns |
Getting started
Reference FluidBus in your project. The FBus facade exposes all modules:
using FluidBus;
FBus.Route(routeEvent); // Router dispatch
FBus.React(reactEvent); // React dispatch
FBus.CallBack("on_complete", someData); // Execute a named callback
FBus.Bench("my scenario", 1000, 100, () => { }); // Benchmark a scenario
FluidBus.Core
Shared foundation used by all modules.
Instructions
Instructions carry the data and logic that handlers execute. They use the FluidCallBack delegate:
public delegate object? FluidCallBack(object? data);
Inherit from FluidInstruction<T>:
using FluidBus.Core.Abstracts;
public class PrintInstruction : FluidInstruction<string>
{
public PrintInstruction(string? data, params FluidCallBack[] methods)
: base(data, methods) { }
}
Each instruction can hold multiple callbacks (deduplicated by ID), executed sequentially. An OnResult event fires after execution.
Protocols
Protocols define the execution strategy for the Router:
public enum ExecutionStrategy { Sync = 0, Async = 1 }
A built-in BusProtocol.System (sync) is always available. Create custom protocols:
using FluidBus.Core.Protocols;
public class AsyncProtocol : BusProtocol
{
public override ExecutionStrategy Strategy => ExecutionStrategy.Async;
public AsyncProtocol() : base("ASYNC") { }
}
Tasks
FluidTask wraps Task.Run() with state tracking (Running, Completed, Failed, Cancelled) and a fluent OnComplete() continuation API.
Error hierarchy
All exceptions inherit from FluidBusError and expose .DisplayMessage().
| Exception | Thrown when |
|---|---|
DispatchException |
Async dispatch fails or unknown ExecutionStrategy |
ProtocolNotFoundException |
Event protocol has no registered port |
HandlerNotFoundException |
No handler registered for a given event type |
DuplicateHandlerException |
Handler already registered for that event type |
InstructionException |
Execute() called with no callbacks or null data |
ChannelException |
Channel write with no subscribers, or subscriber failure |
HandlerLinqException |
Handler registry error |
FluidBus.Router
The Router dispatches events through protocols to registered handlers, matched by event type. One handler per event type.
1. Create a custom event
using FluidBus.Core.Interfaces;
using FluidBus.Core.Protocols;
using FluidBus.Router.Abstracts;
public class UserCreatedEvent : RouteEvent
{
public UserCreatedEvent(string id, BusProtocol protocol, params IFluidInstruction[] instrs)
: base($"{nameof(UserCreatedEvent)}::{id}", protocol, instrs) { }
}
2. Create a custom handler
using FluidBus.Core.Interfaces;
using FluidBus.Router.Abstracts;
public class UserCreatedHandler : RouteHandler<UserCreatedEvent>
{
public UserCreatedHandler(string id)
: base($"{nameof(UserCreatedEvent)}::{id}") { }
public override bool Handle(IFluidEvent evt)
{
Console.WriteLine($"[{Id}] Handling event {evt.Id}");
return base.Handle(evt);
}
}
3. Register and publish
using FluidBus;
using FluidBus.Core.Abstracts;
using FluidBus.Core.Protocols;
using FluidBus.Router.Core;
// Register the handler
FRouter.Register(new UserCreatedHandler("user_handler"));
// Create an instruction with a callback
var instruction = new PrintInstruction("Hello from FluidBus!", msg =>
{
Console.WriteLine(msg);
return null;
});
// Publish on the System protocol (sync)
FBus.Route(new UserCreatedEvent("evt_1", BusProtocol.System, instruction));
Custom protocols
// Register a port for your protocol
FRouter.AddPort(new AsyncProtocol());
// Events using this protocol will dispatch asynchronously
FBus.Route(new UserCreatedEvent("evt_2", new AsyncProtocol(), instruction));
Dispatch flow
FBus.Route(event)
-> FRouter.Publish(event)
-> Lookup port by event.Protocol
-> Lookup handler by event type (HandlerLinq)
-> RouterPort.Dispatch(event, handler)
├─ Sync: event.Dispatch(handler) — blocking
└─ Async: FluidTask wrapping event.Dispatch(handler)
-> handler.Handle(event)
-> Execute each instruction's callbacks sequentially
FluidBus.React
React uses channels (System.Threading.Channels) instead of protocols. Handlers auto-subscribe to their event type's channel on creation. Events are dispatched asynchronously to all subscribers.
1. Create a custom event
using FluidBus.Core.Interfaces;
using FluidBus.React.Abstracts;
public class ChatMessageEvent : ReactEvent
{
public ChatMessageEvent(string id, params IFluidInstruction[] instrs)
: base($"{nameof(ChatMessageEvent)}::{id}", instrs) { }
}
2. Create a custom handler
Handlers subscribe automatically to their channel on instantiation:
using FluidBus.Core.Interfaces;
using FluidBus.React.Abstracts;
public class ChatMessageHandler : ReactHandler<ChatMessageEvent>
{
public ChatMessageHandler(string id)
: base($"{nameof(ChatMessageEvent)}::{id}") { }
public override bool Handle(IFluidEvent evt)
{
Console.WriteLine($"[{Id}] Received message");
return base.Handle(evt);
}
}
3. Register and publish
using FluidBus;
using FluidBus.Core.Abstracts;
using FluidBus.React.Core;
// Instantiating the handler auto-subscribes it to the ChatMessageEvent channel
var handler = new ChatMessageHandler("chat_listener");
// Optionally register it for lifecycle management
FReact.RegisterHandler(handler);
// Publish — all subscribed handlers are notified asynchronously
var instruction = new PrintInstruction("New message!", msg =>
{
Console.WriteLine(msg);
return null;
});
FBus.React(new ChatMessageEvent("msg_1", instruction));
// Drop a handler
FReact.DropHandler(handler);
Dispatch flow
FBus.React(event)
-> FReact.Publish(event)
-> GetOrCreateChannel(event type)
-> channel.Write(event) — enqueued in unbounded Channel<T>
-> Background reader loop (per channel)
-> Broadcast to all ReactReceive subscribers
-> handler.Handle(event)
-> Execute each instruction's callbacks
FluidBus.CallBack
A simple named callback registry using the FluidCallBack delegate.
using FluidBus.Core.Abstracts;
using FluidBus.CallBack.Core;
// Register a callback
FCallBack.RegisterCallBack("on_complete", data =>
{
Console.WriteLine($"Completed with: {data}");
return data;
});
// Execute by name (through the facade)
FBus.CallBack("on_complete", "some result");
// Remove a callback
FCallBack.DropCallBack("on_complete");
Returns null silently if the callback doesn't exist.
FluidBus.Benchmark
Built-in benchmarking with warmup support and nanosecond precision (Stopwatch.GetTimestamp()).
using FluidBus;
using FluidBus.Benchmark.Core;
BenchResult result = FBus.Bench("route 1000 events", iterations: 1000, warmup: 100, () =>
{
FBus.Route(myEvent);
});
result.Print(); // Prints iterations, duration (ms), avg ns/iteration
BenchResult exposes: Iteration, Warmup, Case, Start, End, Duration (ms).
License
Business Source License 1.1 - See LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.