Skip to content

Repository files navigation

Trax.Core

BuildNuGet VersionNuGet Downloads.NETLicense: MITLast CommitcodecovDocs

Railway Oriented Programming for .NET. Build trains that carry data through a sequence of stops, with automatic derailment handling when something goes wrong.

The Trax Stack

Trax is a layered framework split across several repos. You can stop at whatever layer solves your problem. You are here: Trax.Core.

RepoAdds
Trax.CorePipelines, junctions, railway error propagation
Trax.EffectExecution logging, DI, pluggable storage
Trax.MediatorDecoupled dispatch via TrainBus
Trax.SchedulerCron schedules, retries, dead-letter queues
Trax.ApiGraphQL API for remote access
Trax.DashboardBlazor monitoring UI
Trax.Clitrax-cli project scaffolding tool
Trax.SamplesSample apps and a dotnet new template

Full documentation: traxsharp.net/docs.

Why?

Error handling tends to bury the actual logic:

publicasyncTask<OrderReceipt>ProcessOrder(OrderRequestrequest){varinventory=await_inventory.CheckAsync(request.Items);if(!inventory.Available)returnError("Items out of stock");varpayment=await_payments.ChargeAsync(request.PaymentMethod,request.Total);if(!payment.Success)returnError("Payment failed");varshipment=await_shipping.CreateAsync(request.Address,request.Items);if(shipment==null)returnError("Shipping setup failed");returnnewOrderReceipt(payment,shipment);}

Every junction needs its own null check, error branch, and early return. The business logic (check inventory, charge payment, create shipment) gets lost in the noise.

With Trax.Core

publicclassProcessOrderTrain:Train<OrderRequest,OrderReceipt>{protectedoverrideTask<Either<Exception,OrderReceipt>>Junctions()=>Chain<CheckInventoryJunction>().Chain<ChargePaymentJunction>().Chain<CreateShipmentJunction>().Resolve();}

A train picks up its cargo, visits each stop along the route (.Chain<T>), and arrives at its destination (Resolve). If CheckInventoryJunction throws, the train derails and ChargePaymentJunction and CreateShipmentJunction are never reached. The exception propagates through the chain automatically.

Main Track: Input → [Stop 1] → [Stop 2] → [Stop 3] → Output
↓
Derailed: Exception → [Skip] → [Skip] → Exception

Each junction is its own class with its own dependencies, testable in isolation.

Installation

Requires net10.0.

dotnet add package Trax.Core

Quick Start

1. Define a junction. Each junction takes one type of cargo in and produces one type of cargo out:

publicclassValidateEmailJunction(IUserRepositoryrepo):Junction<CreateUserRequest,Unit>{publicoverrideasyncTask<Unit>Run(CreateUserRequestinput){varexisting=awaitrepo.GetByEmailAsync(input.Email);if(existingis not null)thrownewValidationException($"Email {input.Email} is already taken");returnUnit.Default;}}

2. Build a route by chaining junctions into a train:

publicclassCreateUserTrain:Train<CreateUserRequest,User>{protectedoverrideTask<Either<Exception,User>>Junctions()=>Chain<ValidateEmailJunction>().Chain<CreateUserInDatabaseJunction>().Chain<SendWelcomeEmailJunction>().Resolve();}

When the train is run with an input, the cargo is loaded automatically. At each stop, .Chain<T> picks up the cargo T needs from what the train is carrying, runs the junction, and loads the output back on. Resolve unloads the final delivery at the destination.

The train carries all of this in Memory, a type-keyed store that accumulates as the train moves through its route. Each stop can use anything a previous stop produced.

3. Run it:

vartrain=newCreateUserTrain();Either<Exception,User>result=awaittrain.RunEither(request);// Or throw on failure:Useruser=awaittrain.Run(request);

Compile-Time Validation

Trax.Core ships with a Roslyn analyzer that validates your route at build time. If a stop expects cargo that no previous stop has loaded, you get a compiler error, not a runtime derailment.

DiagnosticMeaning
CHAIN001A junction expects cargo that isn't on the train at that point in the route
CHAIN002The train's final delivery type isn't on board when Resolve() is called

IDE Extensions

Inlay hint extensions show TIn → TOut types inline for each .Chain<TJunction>() call, so you can see what cargo flows through each stop at a glance.

  • VSCode: Trax.Core Chain Hints on the Marketplace
  • Rider / ReSharper: Search for Trax.Core Chain Hints in JetBrains Marketplace

Next Layer

When you need execution logging, DI, or persistent metadata, move up to Trax.Effect.

License

MIT

Trademark & Brand Notice

Trax is an open-source .NET framework provided by TraxSharp. This project is an independent community effort and is not affiliated with, sponsored by, or endorsed by the Utah Transit Authority, Trax Retail, or any other entity using the "Trax" name in other industries.

About

Resources

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages