Get started quickly with these practical examples from the repository.
Start Here: Validation Example
Learn the basics:
// Combine multiple validations
var result = FirstName.TryCreate("John")
.Combine(LastName.TryCreate("Doe"))
.Combine(EmailAddress.TryCreate("john@example.com"))
.Bind((first, last, email) => CreateProfile(first, last, email));
// All validations pass → Success
// Any validation fails → Aggregated errorsNext: Try Maybe Examples for optional values
Start Here: User Controller
See how Result maps to HTTP:
[HttpPost("register")]
public ActionResult<User> Register([FromBody] RegisterRequest request) =>
FirstName.TryCreate(request.firstName)
.Combine(LastName.TryCreate(request.lastName))
.Combine(EmailAddress.TryCreate(request.email))
.Bind((first, last, email) => User.TryCreate(first, last, email, request.password))
.ToActionResult(this);
// Success → 200 OK
// Validation → 400 Bad Request
// NotFound → 404 Not FoundFor Minimal API: Check User Routes
Start Here: Order Workflow
See a complete order processing flow:
public async Task<Result<Order>> ProcessOrderAsync(
CustomerId customerId,
List<OrderLineRequest> items,
PaymentInfo paymentInfo)
{
return await Order.TryCreate(customerId)
.BindAsync(order => AddItemsAsync(order, items))
.BindAsync(order => ReserveInventoryAsync(order))
.RecoverOnFailureAsync(
predicate: error => error is ValidationError,
func: async () => await SuggestAlternativesAsync()
)
.Bind(order => order.Submit())
.BindAsync(order => ProcessPaymentAsync(order, paymentInfo))
.TapAsync(order => SendConfirmationEmailAsync(order));
}Key Pattern: Each step validates, next step only runs if previous succeeded
Start Here: Banking Workflow
See fraud detection and secure transactions:
public async Task<Result<BankAccount>> ProcessSecureWithdrawalAsync(
BankAccount account,
Money amount,
string verificationCode)
{
return await account.ToResult()
.EnsureAsync(
async acc => await _fraudDetection.AnalyzeTransactionAsync(acc, amount),
Error.Validation("Fraud check failed")
)
.BindAsync(acc => VerifyMFAIfLargeAmountAsync(acc, amount, verificationCode))
.Bind(acc => acc.Withdraw(amount))
.RecoverOnFailureAsync(
predicate: error => error.Code == "fraud",
func: async error => {
await account.Freeze("Suspicious activity");
return error;
}
);
}Key Pattern: Security checks, MFA, recovery on fraud detection
Email.TryCreate(email)
.Combine(Name.TryCreate(name))
.Combine(Age.TryCreate(age))
.Bind((e, n, a) => CreateUser(e, n, a))Use When: Multiple independent validations needed
await GetUserAsync(id)
.ToResultAsync(Error.NotFound("User not found"))
.EnsureAsync(u => u.IsActive, Error.Validation("Inactive"))
.TapAsync(u => LogAccessAsync(u))
.BindAsync(u => GetOrdersAsync(u))Use When: Chaining async operations
.RecoverOnFailureAsync(
predicate: error => error is UnexpectedError,
func: async () => await RetryOperationAsync()
)Use When: Need retry logic or cleanup on specific errors
var result = await Result.ParallelAsync(
() => GetStudentInfoAsync(studentId),
() => GetStudentGradesAsync(studentId),
() => GetLibraryBooksAsync(studentId)
)
.WhenAllAsync()
.BindAsync((info, grades, books) =>
PrepareReport(info, grades, books));Use When: Multiple independent async operations need to run concurrently
public Result<Order> Submit()
{
return this.ToResult()
.Ensure(_ => Status == OrderStatus.Draft, Error.Validation("Wrong status"))
.Ensure(_ => Lines.Count > 0, Error.Validation("Empty order"))
.Tap(_ => Status = OrderStatus.Pending);
}Use When: Object has states with validation between transitions
| Operation | Purpose | Returns | Use When |
|---|---|---|---|
Bind |
Chain operations that can fail | Result<TOut> |
Next operation needs Result value |
Map |
Transform value | Result<TOut> |
Simple transformation, no failure |
Ensure |
Validate condition | Result<T> |
Business rule validation |
Tap |
Side effect on success | Result<T> |
Logging, metrics, notifications |
Combine |
Merge multiple Results | Result<(T1,T2,...)> |
Multiple independent validations |
RecoverOnFailure |
Fallback on failure | Result<T> |
Retry, cleanup, alternative path |
Match |
Unwrap Result | TOut |
End of chain, handle both success and failure |
Async Variants: BindAsync, MapAsync, EnsureAsync, TapAsync, RecoverOnFailureAsync, MatchAsync
[Fact]
public void Order_Creation_Success()
{
var result = Order.TryCreate(customerId)
.Bind(order => order.AddLine(productId, "Item", price, 1))
.Bind(order => order.Submit());
result.IsSuccess.Should().BeTrue();
result.Value.Status.Should().Be(OrderStatus.Pending);
}[Fact]
public void Order_Creation_Fails_With_Invalid_Email()
{
var result = EmailAddress.TryCreate("invalid-email")
.Bind(email => CreateOrder(email));
result.IsFailure.Should().BeTrue();
result.Error.Should().BeOfType<ValidationError>();
}[Fact]
public async Task Payment_Failure_Triggers_Inventory_Release()
{
// Arrange: Create order with reserved inventory
var order = CreateOrderWithReservedInventory();
// Act: Process payment (will fail)
var result = await workflow.ProcessOrderAsync(order, invalidPaymentInfo);
// Assert: Inventory was released
result.IsFailure.Should().BeTrue();
inventoryService.GetStock(productId).Should().Be(originalStock);
}// Be specific with errors
return Error.Validation("Email format is invalid", "email");
// Provide context
return Error.NotFound($"Order {orderId} not found");
// Use appropriate error types
if (unauthorized) return Error.Unauthorized("Login required");
if (forbidden) return Error.Forbidden("Insufficient permissions");// Don't use generic errors
return Error.Unexpected("Something went wrong");
// Don't swallow errors
try { /* ... */ } catch { return Error.Unexpected("Error"); }
// Don't throw exceptions in ROP code
if (invalid) throw new Exception(); // Use Result.Failure instead- Read the README: Start with Examples README for full overview
- Pick a Learning Path: Follow the Complexity Guide
- Run Examples: Execute example code and experiment
- Read the Docs: Check Railway Oriented Programming
- Concepts: Read The Basics
- API Reference: See Railway Oriented Programming README
- Integration: Check ASP.NET Integration
- Issues: Open a GitHub issue