Trellis
Structured building blocks for AI-driven enterprise software
Write less code that reads like English using Railway-Oriented Programming and Domain-Driven Design for .NET 10.
Before & After
Traditional Approach
// 20 lines of repetitive error checking — easy to miss a check!
var firstName = ValidateFirstName(input.FirstName);
if (firstName == null) return BadRequest("Invalid first name");
var lastName = ValidateLastName(input.LastName);
if (lastName == null) return BadRequest("Invalid last name");
var email = ValidateEmail(input.Email);
if (email == null) return BadRequest("Invalid email");
var user = CreateUser(firstName, lastName, email);
if (user == null) return BadRequest("Cannot create user");
if (!_repository.EmailExists(email))
return Conflict("Email already registered");
_repository.Save(user);
_emailService.SendWelcome(user.Email);
return Ok(user);
With Trellis
// 8 lines — reads like a story: validate → create → check → save → notify
return FirstName.TryCreate(input.FirstName)
.Combine(LastName.TryCreate(input.LastName))
.Combine(EmailAddress.TryCreate(input.Email))
.Bind((first, last, email) => User.TryCreate(first, last, email))
.Ensure(user => !_repository.EmailExists(user.Email), Error.Conflict("Email registered"))
.Tap(user => _repository.Save(user))
.Tap(user => _emailService.SendWelcome(user.Email))
.Match(onSuccess: user => Ok(user), onFailure: error => BadRequest(error.Detail));
60% less code. Reads like English. Impossible to skip error handling.
What You Get
- Result<T> and Maybe<T> — composable error handling and optional values. No exceptions, no null. Learn more
- Type-safe value objects —
FirstName,EmailAddress,OrderIdgenerated from one-line declarations. If it exists, it's valid. Learn more - Aggregates and entities — DDD building blocks with domain events and consistency boundaries. Learn more
- 10 discriminated error types —
ValidationError,NotFoundError,ConflictError, etc. Map automatically to HTTP status codes. Learn more - 9 pipeline operations —
Bind,Map,Tap,Ensure,Combine,Match, and more. All with async variants. Learn more - 19 Roslyn analyzers — catch unsafe
.Valueaccess, forgotten results, and anti-patterns at compile time. Learn more - State machine integration —
FireResult()returnsResult<T>instead of throwing. Learn more - AI-ready patterns — structured building blocks that AI can generate correctly. Learn more
Quick Start
dotnet add package Trellis.Results
dotnet add package Trellis.Primitives
dotnet add package Trellis.Primitives.Generator
dotnet add package Trellis.Analyzers
using Trellis;
// Define a value object — one line
public partial class EmailAddress : RequiredString { }
// Use it — invalid values are impossible
Result<EmailAddress> result = EmailAddress.TryCreate("user@example.com");
result.Match(
onSuccess: email => Console.WriteLine($"Valid: {email}"),
onFailure: error => Console.WriteLine($"Error: {error.Detail}")
);
See the Introduction for a full walkthrough, or jump to Basics for all pipeline operations.
Packages
Core
| Package | Purpose |
|---|---|
Trellis.Results |
Result<T>, Maybe<T>, error types, pipeline operations, async support |
Trellis.DomainDrivenDesign |
Aggregate, Entity, ValueObject, Domain Events |
Trellis.Primitives |
RequiredString, RequiredGuid, RequiredInt, RequiredDecimal + 12 ready-to-use value objects |
Trellis.Primitives.Generator |
Source generator for value object boilerplate |
Trellis.Analyzers |
19 Roslyn analyzers enforcing ROP best practices at compile time |
Integration
| Package | Purpose |
|---|---|
Trellis.Asp |
Result → HTTP responses: ToActionResult() for MVC, ToHttpResult() for Minimal API, configurable error mapping |
Trellis.Http |
HttpClient extensions returning Result<T> |
Trellis.FluentValidation |
Bridge FluentValidation errors to Result<T> |
Trellis.Testing |
FluentAssertions extensions, test builders, fakes |
Trellis.Stateless |
Wraps Stateless state machine Fire() to return Result<T> |
Performance
Trellis adds 11–16 nanoseconds per operation — 0.002% of a typical database query. Zero extra allocations on Combine.
See Performance & Benchmarks for detailed analysis.
The Vision
Trellis is designed so that both humans and AI can produce correct, maintainable, enterprise-grade code by following the structure the framework provides.
- A human writes a specification describing business requirements in plain language.
- An AI produces enterprise software using Trellis as the structural foundation.
- Domain terms map directly to Trellis constructs — aggregates, value objects, entities, domain events, state machines.
- The type system and compiler enforce correctness — impossible to skip error handling or make illegal state transitions.
- When requirements change, changes propagate correctly through the type system.
See Trellis for AI Code Generation for details on spec-to-code mapping.
Learn More
| Path | Start Here |
|---|---|
| New to Trellis | Introduction → Basics → Examples |
| Integrating | ASP.NET Core · HTTP Client · EF Core · FluentValidation |
| Architecture | Clean Architecture · Aggregate Factory Pattern |
| Reference | API Documentation · Analyzer Rules · Debugging |
| Migrating | Migration Guide from FunctionalDDD |