Integration
Integrate FunctionalDDD with popular .NET frameworks and tools for building production-ready applications with Railway-Oriented Programming.
Overview
This section provides comprehensive guides for integrating the FunctionalDDD library with:
- ASP.NET Core - MVC Controllers and Minimal API
- HTTP Client - HttpClient extensions for Result/Maybe patterns
- FluentValidation - Powerful validation framework
- Entity Framework Core - ORM and repository patterns
- OpenTelemetry - Distributed tracing and observability
- Problem Details - Standard error response format (RFC 7807)
Each integration guide includes installation instructions, configuration examples, best practices, and complete working code samples.
Integration Guides
🌐 ASP.NET Core Integration
Level: Intermediate | Time: 30-45 min
Learn how to integrate Railway-Oriented Programming with ASP.NET Core:
- MVC Controllers -
ToActionResultfor controllers - Minimal API -
ToHttpResultfor endpoints - Automatic Error Mapping - Error types → HTTP status codes
- Pagination Support - HTTP 206 Partial Content responses
- Custom Error Responses -
MatchErrorfor fine-grained control
Key Features:
- ✅ Automatic status code mapping (ValidationError → 400, NotFoundError → 404, etc.)
- ✅ Problem Details (RFC 7807) format
- ✅ Field-level validation errors
- ✅ Unit type support (204 No Content)
- ✅ Full async/await with CancellationToken
🌐 HTTP Client Integration
Level: Beginner | Time: 20-30 min
Work with HttpClient using functional patterns:
- Status Code Handlers - Handle 401, 403, 404, 409 errors functionally
- Range Handlers - Handle all 4xx or 5xx errors at once
- JSON Deserialization - Convert responses to
Result<T>orResult<Maybe<T>> - Functional Error Handling - No exceptions, just Result types
- Railway Composition - Chain HTTP calls with other operations
Key Features:
- ✅ Specific status code handling (HandleUnauthorized, HandleForbidden, HandleConflict)
- ✅ Range-based error handling (HandleClientError, HandleServerError)
- ✅ EnsureSuccess - Functional alternative to EnsureSuccessStatusCode()
- ✅ JSON deserialization with Result/Maybe
- ✅ Full async/await with CancellationToken
✅ FluentValidation Integration
Level: Intermediate | Time: 30-40 min
Integrate FluentValidation for powerful, composable validation:
- Inline Validators - Simple validation within aggregates
- Separate Validator Classes - Complex validation logic
- Async Validation - Database uniqueness checks, external service calls
- Dependency Injection - Register validators with ASP.NET Core DI
- Advanced Patterns - Conditional validation, custom validators, cascading failures
Key Features:
- ✅ Converts FluentValidation results to
Result<T> - ✅ Rich validation rule set
- ✅ Automatic ValidationError formatting
- ✅ Async validation support
- ✅ Seamless DI integration
💾 Entity Framework Core Integration
Level: Intermediate | Time: 30-40 min
Build type-safe repository patterns with EF Core:
- Repository Pattern - Repositories that return
Result<T> - Extension Methods - Convert nullable to Result
- Exception Handling - Map database exceptions to error types
- Pagination - Paginated results with EF Core
- Value Object Configuration - Configure value objects in EF Core
Key Features:
- ✅ No more repository exceptions
- ✅ Explicit error handling
- ✅ Database exception mapping (conflicts, concurrency, etc.)
- ✅ Value object conversions
- ✅ Type-safe queries
📊 Observability & Monitoring
Level: Advanced | Time: 20-30 min
Enable distributed tracing and monitoring:
- OpenTelemetry Tracing - Automatic ROP and Value Object tracing
- Problem Details (RFC 7807) - Standard error response format
- Trace Correlation - Link HTTP errors to distributed traces
- Production Monitoring - Set up observability in production
Key Features:
- ✅ Automatic span creation for ROP operations
- ✅ Detailed trace attributes (error types, timings, etc.)
- ✅ Compatible with Jaeger, Zipkin, Application Insights
- ✅ Problem Details with trace IDs
- ✅ Error rate monitoring
Quick Start
1. Install Packages
# ASP.NET Core integration
dotnet add package FunctionalDDD.Asp
# HttpClient integration
dotnet add package FunctionalDDD.HttpClient
# FluentValidation integration
dotnet add package FunctionalDDD.FluentValidation
dotnet add package FluentValidation
# For OpenTelemetry tracing
dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
2. Configure Services (Program.cs)
using FunctionalDdd;
using FluentValidation;
using OpenTelemetry.Trace;
using Mediator;
var builder = WebApplication.CreateBuilder(args);
// Add controllers or minimal API
builder.Services.AddControllers();
// or
builder.Services.AddEndpointsApiExplorer();
// Register Mediator (OSS alternative to MediatR)
builder.Services.AddMediator(options =>
{
options.ServiceLifetime = ServiceLifetime.Scoped;
});
// Register FluentValidation validators
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
// Register EF Core
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// Register repositories
builder.Services.AddScoped<IUserRepository, UserRepository>();
// Add OpenTelemetry tracing
builder.Services.AddOpenTelemetry()
.ConfigureResource(resource => resource.AddService("MyApplication"))
.WithTracing(tracerBuilder =>
{
tracerBuilder
.AddFunctionalDddRopInstrumentation()
.AddFunctionalDddCvoInstrumentation()
.AddAspNetCoreInstrumentation()
.AddEntityFrameworkCoreInstrumentation()
.AddOtlpExporter();
});
var app = builder.Build();
app.MapControllers();
app.Run();
3. Use in Controllers
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IMediator _mediator;
public UsersController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<ActionResult<UserDto>> Register(
[FromBody] RegisterUserRequest request,
CancellationToken ct)
=> await FirstName.TryCreate(request.FirstName)
.Combine(LastName.TryCreate(request.LastName))
.Combine(EmailAddress.TryCreate(request.Email))
.Bind((firstName, lastName, email) =>
RegisterUserCommand.TryCreate(firstName, lastName, email))
.BindAsync(command => _mediator.Send(command, ct), ct)
.MapAsync(user => user.Adapt<UserDto>())
.ToActionResultAsync(this);
}
Complete Architecture Example
Here's how all the integrations work together:
┌─────────────────────────────────────────────────────────────┐
│ API Layer (ASP.NET Core) │
│ Controllers/Endpoints → ToActionResult/ToHttpResult │
└────────────────────────┬────────────────────────────────────┘
│
┌────────────────────────┼────────────────────────────────────┐
│ Application Layer (Mediator) │
│ Commands/Queries → FluentValidation → Result<T> │
└────────────────────────┬────────────────────────────────────┘
│
┌────────────────────────┼────────────────────────────────────┐
│ Domain Layer │
│ Aggregates, Value Objects, Business Rules │
└────────────────────────┬────────────────────────────────────┘
│
┌────────────────────────┼────────────────────────────────────┐
│ Infrastructure Layer (EF Core) │
│ Repositories → Result<T> (no exceptions) │
└──────────────────────────────────────────────────────────────┘
All layers traced with OpenTelemetry
Best Practices
1. Keep Result Internal
Convert to HTTP responses only at the API boundary:
// ✅ Good - Result stays in application layer
public class UserService
{
public async Task<Result<User>> CreateUserAsync(
CreateUserCommand command,
CancellationToken ct) { /* ... */ }
}
[HttpPost]
public async Task<ActionResult<UserDto>> CreateUser(
CreateUserRequest request,
CancellationToken ct) =>
await _userService.CreateUserAsync(request.ToCommand(), ct)
.ToActionResultAsync(this); // Convert at boundary
2. Validate Early
Use FluentValidation at the edge of your system:
public async Task<Result<Order>> CreateOrderAsync(
CreateOrderCommand command,
CancellationToken ct)
{
// Validate first, fail fast
return await _validator.ValidateToResultAsync(command, ct)
.BindAsync((validCmd, cancellationToken) =>
ProcessOrderAsync(validCmd, cancellationToken), ct);
}
3. Repository Returns Result
No more repository exceptions:
// ✅ Good
public async Task<Result<User>> GetByIdAsync(UserId id, CancellationToken ct)
{
var user = await _context.Users.FindAsync(id, ct);
return user.ToResult(Error.NotFound($"User {id} not found"));
}
4. Enable Tracing in Production
Monitor errors and performance:
builder.Services.AddOpenTelemetry()
.WithTracing(tracerBuilder =>
{
tracerBuilder
.AddFunctionalDddRopInstrumentation()
.AddOtlpExporter();
});
5. Always Pass CancellationToken
Support graceful cancellation:
public async Task<Result<Order>> ProcessOrderAsync(
CreateOrderCommand command,
CancellationToken ct) // ✅ Accept token
=> await _validator.ValidateToResultAsync(command, ct) // ✅ Pass through
.BindAsync((cmd, cancellationToken) =>
CreateOrderAsync(cmd, cancellationToken), ct); // ✅ Pass to all async ops
Error Response Examples
Validation Error (400)
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"email": ["Email is required"],
"age": ["Must be 18 or older"]
}
}
Not Found Error (404)
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.4",
"title": "Not Found",
"status": 404,
"detail": "User 12345 not found",
"instance": "/api/users/12345"
}
Conflict Error (409)
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.8",
"title": "Conflict",
"status": 409,
"detail": "Email already registered"
}
Next Steps
- Start with ASP.NET Core Integration to learn the basics
- Add validation with FluentValidation Integration
- Implement repositories using Entity Framework Core Integration
- Enable monitoring with Observability & Monitoring
For complete working examples, see:
- Examples - Real-world code samples
- Error Handling - Working with different error types
- Debugging - Tools and techniques for debugging ROP chains