Executables 1.0.0
dotnet add package Executables --version 1.0.0
NuGet\Install-Package Executables -Version 1.0.0
<PackageReference Include="Executables" Version="1.0.0" />
<PackageVersion Include="Executables" Version="1.0.0" />
<PackageReference Include="Executables" />
paket add Executables --version 1.0.0
#r "nuget: Executables, 1.0.0"
#:package Executables@1.0.0
#addin nuget:?package=Executables&version=1.0.0
#tool nuget:?package=Executables&version=1.0.0
Executables
Build application logic once - run it with different runtime behavior.
Executables is a composable .NET library for modeling application behavior as reusable, strongly typed operations.
It separates:
- what your code does: executable composition,
- how your code runs: runtime policies, branching, context, error handling, and execution control.
That means the same logic can be reused across different application boundaries and executed with different runtime rules without rewriting the core flow.
Why Executables
In real applications, behavior often gets scattered across many places:
- business logic in services,
- validation in validators,
- retries and timeouts in wrappers,
- error handling in middleware,
- branching logic inside large methods,
- commands, queries, and event handlers as separate API shapes.
Over time, that makes execution flow harder to reuse, test, adapt, and reason about.
Executables gives you one explicit composition model for that behavior. It becomes useful when direct method calls
are no longer enough because the same logic needs to be:
- composable,
- reusable across multiple entry points,
- decorated with runtime rules,
- strongly typed from input to output,
- easy to test in isolation.
Typical fits include:
- application-layer orchestration,
- validation-heavy workflows,
- query and command pipelines,
- explicit branching and fallback flows,
- policy-based execution with retry, timeout, guards, and context propagation.
With it, you can define logic once, compose it from small typed steps, expose it as a query, command, event subscriber, or handler, and decide later how it should execute at runtime.
The Core Idea
The library revolves around one main split:
IExecutable<TIn, TOut>describes behavior as a reusable typed value,IExecutor<TIn, TOut>runs that behavior with runtime decoration and execution control.
That split is the main reason the library exists. It lets you keep composition stable while changing runtime behavior.
The same executable chain can be executed with validation, fallback, timeout, retry, exception mapping, context initialization, result wrapping, caching, metrics, or middleware pipelines without changing the original composition.
Define behavior once. Run it many ways.
Quick Example
IExecutor<string, Result<int>> parseLength =
Executable.Create((string text) => text.Trim())
.Then(text => text.Length)
.GetExecutor()
.WithPolicy(policy => policy
.ValidateInput(text => !string.IsNullOrWhiteSpace(text), "Value is required")
.ValidateOutput(length => length >= 0, "Length must be non-negative"))
.WithResult();
Result<int> result = parseLength.Execute(" demo ");
What happens here:
Executable.Create(...)wraps behavior as a reusable executable unit.Then(...)composes the next step.GetExecutor()moves from composition to execution.WithPolicy(...)adds runtime validation.WithResult()turns success and failure into an explicit return contract.
The important part is that the executable composition itself stays reusable.
Reusing The Same Composition
IExecutable<string, int> parse =
Executable.Create((string text) => text.Trim())
.Then(text => int.Parse(text));
IExecutor<string, Result<int>> safe =
parse
.GetExecutor()
.WithPolicy(policy => policy
.ValidateInput(text => !string.IsNullOrWhiteSpace(text), "Value is required")
.Fallback<FormatException>((_, _) => 0))
.WithResult();
IExecutor<string, Optional<int>> forgiving =
parse
.GetExecutor()
.SuppressException()
.OfType<FormatException>();
This is the same composition executed through two different runtime contracts:
safereturnsResult<int>,forgivingreturnsOptional<int>.
The logic is the same. Only the runtime changes.
Main Building Blocks
Executables gives you one model that covers reusable executable units, pipeline composition, explicit branching,
commands and queries, event-driven flows, executor-level policies, middleware-style runtime pipelines, and aligned sync
and async APIs.
Executable Composition
Use Executable.Create(...), Then(...), Compose(...), Fork(...), Merge(...), FlatMap(...), Map(...), and
related operators to build reusable behavior.
IExecutable<string, string> pipeline =
Executable.Create((string text) => text.Trim())
.Then(text => text.ToUpperInvariant())
.Then(text => $"Value: {text}");
Runtime Execution and Policies
Convert a composition into an executor and decorate it with runtime behavior such as validation, guards, fallback, timeout, retry, reentrancy prevention, or completion-driven cancellation.
IAsyncExecutor<string, Result<int>> runtime =
AsyncExecutable.Create(async (string text, CancellationToken token) =>
{
await Task.Delay(10, token);
return int.Parse(text);
})
.GetExecutor()
.WithPolicy(policy => policy
.ValidateInput(text => !string.IsNullOrWhiteSpace(text), "Value is required")
.Timeout(TimeSpan.FromSeconds(1)))
.WithResult();
Queries, Commands, and Events
Expose executable logic through more specific application-oriented contracts when you want clearer intent at the API boundary.
IQuery<string, string> query = pipeline.AsQuery();
ICommand<string> command =
Executable.Create((string text) => !string.IsNullOrWhiteSpace(text))
.AsCommand();
var changed = new Event<string>();
changed.Subscribe(value => Console.WriteLine($"Changed: {value}"));
changed.Handle(EventPublisher.Sequential<string>());
changed.Publish("Updated");
Middleware Pipelines
Build runtime execution through middleware-style chains when that shape is more natural than isolated policies.
IExecutor<string, string> executor = Pipeline
.Use((string text, Func<string, int> next) =>
{
string normalized = text.Trim();
int length = next(normalized);
return $"Length: {length}";
})
.End(Executable.Create((string text) => text.Length));
Sync and Async APIs
The library keeps synchronous and asynchronous APIs structurally parallel, so moving to async does not require learning a different model.
IExecutable<string, int> parse = Executable.Create((string text) => int.Parse(text));
IAsyncExecutable<string, string> asyncPipeline =
parse.Then(async (int value, CancellationToken token) =>
{
await Task.Delay(1, token);
return $"Value: {value}";
});
Why Not Just Use Methods, Delegates, or Middleware?
Direct methods are often enough for small, local, straightforward code. Executables becomes useful when behavior
needs to be extracted from ad hoc call chains, shared across different application boundaries, composed from reusable
steps, executed with different runtime policies, or adapted without rewriting the core logic.
Delegates are a good low-level representation of behavior, but they do not by themselves give you a consistent composition API, reusable runtime decoration, specialized contracts like queries and commands, structured branching, policy pipelines, reversible typed adaptation, or aligned sync and async executable models.
Middleware and resilience libraries solve important parts of the problem, but usually from specific angles.
Executables models application behavior itself as typed composable values and then lets you apply runtime control
around that behavior. Depending on the use case, these approaches can complement each other.
When to Use It
Executables fits especially well when application behavior should be:
- explicit,
- strongly typed,
- reusable,
- testable in isolation,
- adaptable to multiple runtime environments,
- composed without committing to a heavy framework.
Good fits include:
- application services,
- orchestration layers,
- command and query flows,
- reusable workflow steps,
- boundary-heavy code with validation and fallback logic,
- explicit runtime control around execution.
When Not to Use It
Executables is probably not the right tool when:
- direct methods already express behavior clearly,
- the application is very small and simple,
- composition is not important,
- runtime decoration is minimal,
- introducing an explicit abstraction layer would add more complexity than value.
It is also not:
- a DI container,
- a distributed messaging system,
- a reactive stream library,
- a framework that dictates your entire architecture.
Installation
dotnet add package Executables
Supported target frameworks:
net9.0netstandard2.1net48
Documentation
Local Documentation
The repository uses a local DocFX tool manifest.
dotnet tool restore
dotnet docfx docs\docfx.json --serve
Project Files
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| .NET Framework | net48 is compatible. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.8
- System.Threading.Tasks.Extensions (>= 4.6.3)
-
.NETStandard 2.1
- System.Threading.Tasks.Extensions (>= 4.6.3)
-
net9.0
- System.Threading.Tasks.Extensions (>= 4.6.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 108 | 4/9/2026 |
Initial release.