.NET Libraries
The composable foundation everything else in the ecosystem is built on. Eight libraries that progress from raw control flow (Result) to construction (Builder), to runtime substrates (FiniteStateMachine, Wrapper.Versioning, BinaryWrapper), to the four DSL frameworks (Dsl, Ddd, Requirements, Diem).
Result (FrenchExDev.Net.Result)
Explicit success/failure values instead of exceptions for control flow. Three sealed record variants — Result, Result<T>, Result<T, TError> — with composable operations that chain naturally:
Result<User> result = await GetUserAsync(id)
.Map(user => user with { LastLogin = DateTime.UtcNow })
.Bind(user => ValidateUser(user))
.Recover(err => Log.Error(err, "Validation failed"));Result<User> result = await GetUserAsync(id)
.Map(user => user with { LastLogin = DateTime.UtcNow })
.Bind(user => ValidateUser(user))
.Recover(err => Log.Error(err, "Validation failed"));Map transforms success values. Bind chains operations that themselves return Results. Recover handles failures. Tap and Ensure round out the pipeline. Every operation preserves the error channel — no silent swallowing, no thrown exceptions, no ambiguity about what went wrong.
See the dedicated article for the full API and design rationale.
Builder (FrenchExDev.Net.Builder)
Source-generator-powered async object construction for domain models that need validation, async resolution, or circular reference detection before they can be instantiated.
[Builder]
public partial record Order
{
public required string CustomerId { get; init; }
public required List<OrderItem> Items { get; init; }
public required decimal Total { get; init; }
}
[Builder]
public partial record OrderItem
{
public required int Quantity { get; init; }
public required int Currency { get; init; }
public required double Price { get; init; }
public required string Sku { get; init; }
}
// Generated: OrderBuilder with fluent API, validation hooks, and async Build()
var order = await new OrderBuilder()
.WithCustomerId("C-42")
.WithItems(items => items
.Add(i => i.WithSku("WIDGET-01").WithQuantity(3))
.Add(i => i.WithSku("GADGET-07").WithQuantity(1)))
.BuildAsync(ct);[Builder]
public partial record Order
{
public required string CustomerId { get; init; }
public required List<OrderItem> Items { get; init; }
public required decimal Total { get; init; }
}
[Builder]
public partial record OrderItem
{
public required int Quantity { get; init; }
public required int Currency { get; init; }
public required double Price { get; init; }
public required string Sku { get; init; }
}
// Generated: OrderBuilder with fluent API, validation hooks, and async Build()
var order = await new OrderBuilder()
.WithCustomerId("C-42")
.WithItems(items => items
.Add(i => i.WithSku("WIDGET-01").WithQuantity(3))
.Add(i => i.WithSku("GADGET-07").WithQuantity(1)))
.BuildAsync(ct);The [Builder] attribute triggers a Roslyn incremental generator that emits fluent With*() methods, per-property validation overrides, BuilderList<T, TBuilder> for nested collections, DictionaryBuilder<K, V>, and thread-safe single-flight caching via SemaphoreSlim. Two modes: simple construction, or exception-aware with [Builder(Exception = typeof(...))] for builders that surface typed build errors.
See the dedicated article for the full deep-dive.
BinaryWrapper (FrenchExDev.Net.BinaryWrapper)
The flagship project. Turns any CLI binary into a fully typed C# API by scraping --help output, serializing command trees to JSON, and feeding them to a Roslyn source generator that emits command classes, fluent builders, and typed clients — with multi-version awareness and runtime version guards.
[BinaryWrapper("packer", FlagPrefix = "-", FlagValueSeparator = "=", UseBoolEqualsFormat = true)]
public partial class PackerDescriptor; // 6 lines. The generator does the rest.
// At runtime: full IntelliSense, every flag is a typed property
var cmd = client.Build(b => b
.WithForce(true)
.WithParallelBuilds(4)
.WithVar(["region=us-east-1"])
.WithTemplate("template.pkr.hcl"));[BinaryWrapper("packer", FlagPrefix = "-", FlagValueSeparator = "=", UseBoolEqualsFormat = true)]
public partial class PackerDescriptor; // 6 lines. The generator does the rest.
// At runtime: full IntelliSense, every flag is a typed property
var cmd = client.Build(b => b
.WithForce(true)
.WithParallelBuilds(4)
.WithVar(["region=us-east-1"])
.WithTemplate("template.pkr.hcl"));The three-phase architecture (design-time scraping, build-time generation, runtime execution) means each concern evolves independently. Five pluggable help parsers cover GNU, cobra, argparse, and custom formats. VersionDiffer.Merge() computes [SinceVersion]/[UntilVersion] annotations across all scraped versions, and generated builders inject VersionGuard checks that throw clear exceptions instead of letting a wrong flag silently fail in stderr.
See the dedicated article for the full deep-dive.
Wrapper.Versioning (FrenchExDev.Net.Wrapper.Versioning)
A generic design pipeline framework for downloading, transforming, and saving versioned items from GitHub and GitLab API endpoints. Handles pagination (via Link header parsing), rate limiting, filtering, and parallel processing through System.Threading.Channels.
await new DesignPipelineRunner<string>
{
ItemCollector = new GitHubReleasesVersionCollector("containers", "podman"),
Pipeline = new DesignPipeline<string>()
.UseHttpDownload(v => $"https://example.com/{v}/schema.json")
.UseContentTransform((v, content) => Minify(content))
.UseSave()
.Build(),
KeySelector = v => v,
OutputDir = "output/",
}.RunAsync(["--parallel", "4", "--min-version", "4.0.0"]);await new DesignPipelineRunner<string>
{
ItemCollector = new GitHubReleasesVersionCollector("containers", "podman"),
Pipeline = new DesignPipeline<string>()
.UseHttpDownload(v => $"https://example.com/{v}/schema.json")
.UseContentTransform((v, content) => Minify(content))
.UseSave()
.Build(),
KeySelector = v => v,
OutputDir = "output/",
}.RunAsync(["--parallel", "4", "--min-version", "4.0.0"]);Provides the foundation for BinaryWrapper's design-time scraping, DockerCompose.Bundle schema downloading, and Traefik.Bundle schema processing. CLI flags: --parallel, --min-version, --missing, --dashboard.
Finite State Machine (FrenchExDev.Net.FiniteStateMachine)
Production-grade async FSM framework with three tiers — Dynamic (string-based), Typed (enum-based), and Rich (OOP with computed transitions). Guards, entry/exit actions, hierarchical states, deferred events, parallel regions, timers, graph introspection, and Mermaid export:
var fsm = new FiniteStateMachine<OrderState, OrderEvent>(OrderState.Pending)
.AddTransition(OrderState.Pending, OrderEvent.Confirm, OrderState.Confirmed)
.AddTransition(OrderState.Confirmed, OrderEvent.Ship, OrderState.Shipped);
await fsm.TriggerAsync(OrderEvent.Confirm);
// State is now OrderState.Confirmedvar fsm = new FiniteStateMachine<OrderState, OrderEvent>(OrderState.Pending)
.AddTransition(OrderState.Pending, OrderEvent.Confirm, OrderState.Confirmed)
.AddTransition(OrderState.Confirmed, OrderEvent.Ship, OrderState.Shipped);
await fsm.TriggerAsync(OrderEvent.Confirm);
// State is now OrderState.ConfirmedAll three tiers return Result<Transition<TState>> — no exceptions for denied transitions.
See the dedicated article for the full architecture including source generation, testing utilities, and model-based path generation.
Dsl — Meta-Metamodel (FrenchExDev.Net.Dsl)
The M3 foundation that all DSLs are built from. Five self-describing primitives — [MetaConcept], [MetaProperty], [MetaReference], [MetaConstraint], [MetaInherits] — equivalent to OMG MOF / Eclipse EMF Ecore but native C# with compile-time validation.
// M3 primitives define themselves (fixed point)
[MetaConcept("MetaConcept")]
public sealed class MetaConceptAttribute : Attribute
{
[MetaProperty("Name", Required = true)]
public string Name { get; }
}// M3 primitives define themselves (fixed point)
[MetaConcept("MetaConcept")]
public sealed class MetaConceptAttribute : Attribute
{
[MetaProperty("Name", Required = true)]
public string Name { get; }
}Behavioral companions extend a MetaConcept base class with constraint methods (type-safe validation, not string expressions). A Roslyn incremental source generator auto-discovers all concepts and emits a MetamodelRegistry. Zero external dependencies — this is the root of the DSL dependency graph.
See the dedicated article for the full M0–M3 architecture.
Ddd — Domain-Driven Design DSL (FrenchExDev.Net.Ddd)
An M2 DSL built on Dsl for implementing DDD tactical patterns. Developers annotate partial classes with 13 attributes — [AggregateRoot], [Entity], [ValueObject], [Command], [DomainEvent], [Property], [Composition], [Association], [Invariant], and more — and a Roslyn source generator validates against metamodel constraints and produces entity implementations, builders, EF Core configurations, repositories, and CQRS handlers.
[AggregateRoot]
public partial class Order
{
[Property("CustomerId", Required = true)]
public partial string CustomerId { get; }
[Composition(typeof(OrderLine))]
public partial IReadOnlyList<OrderLine> Lines { get; }
[Invariant]
public Result<Order, InvalidOperationException> MustHaveAtLeastOneLine()
{
if (!Lines.Any())
return Result<Order, InvalidOperationException>.Failure(
new InvalidOperationException("Order must have at least one line"));
return Result<Order, InvalidOperationException>.Success(this);
}
}[AggregateRoot]
public partial class Order
{
[Property("CustomerId", Required = true)]
public partial string CustomerId { get; }
[Composition(typeof(OrderLine))]
public partial IReadOnlyList<OrderLine> Lines { get; }
[Invariant]
public Result<Order, InvalidOperationException> MustHaveAtLeastOneLine()
{
if (!Lines.Any())
return Result<Order, InvalidOperationException>.Failure(
new InvalidOperationException("Order must have at least one line"));
return Result<Order, InvalidOperationException>.Success(this);
}
}See the dedicated article for DDD patterns and the CMF article for the full-stack code generation pipeline.
Requirements — Feature Tracking DSL (FrenchExDev.Net.Requirements)
An M2 DSL built on Dsl for type-safe requirement traceability. Requirements are abstract classes. Acceptance criteria are abstract methods. The compiler refuses to build until every AC has a specification, an implementation, and a test — all linked by typeof() and nameof(), not strings.
public abstract class UserRoleManagement : Feature<EnterprisePlatform>
{
public abstract AcceptanceCriterionResult AdminCanAssignRoles(UserId admin, RoleId role);
public abstract AcceptanceCriterionResult UserCanViewOwnRoles(UserId user);
}
// Specification: compiler enforces method signatures match the ACs
[ForRequirement(typeof(UserRoleManagement))]
public interface IRoleAssignmentSpec
{
Result<User, DomainException> AssignRole(UserId admin, RoleId role);
}public abstract class UserRoleManagement : Feature<EnterprisePlatform>
{
public abstract AcceptanceCriterionResult AdminCanAssignRoles(UserId admin, RoleId role);
public abstract AcceptanceCriterionResult UserCanViewOwnRoles(UserId user);
}
// Specification: compiler enforces method signatures match the ACs
[ForRequirement(typeof(UserRoleManagement))]
public interface IRoleAssignmentSpec
{
Result<User, DomainException> AssignRole(UserId admin, RoleId role);
}A Roslyn source generator emits a RequirementRegistry, and analyzers report diagnostics (REQ100–REQ302) for missing specs, unlinked implementations, and untested ACs.
See the dedicated article for the full chain from feature definition to end-to-end testing.
Diem — Content Management Framework (FrenchExDev.Net.Diem)
A 62-project CMF delivering four sub-DSLs on top of Dsl and Ddd: Content (parts, blocks, StreamFields), Admin (auto-generated CRUD lists, forms, actions), Pages (layouts, zones, widgets, dynamic routing), and Workflow (state machines, gates, scheduling, per-locale progress). From attribute-decorated models, the compiler produces Blazor components, EF Core persistence, REST + GraphQL APIs, and a workflow engine.
Draws from Orchard Core (content parts), Wagtail (StreamFields), Drupal (versioning/taxonomy), and the original 2010 PHP Diem project (page composition model). Includes a cmf CLI for scaffolding, generation, validation, and migration.
See the dedicated article for the full architecture.