dotnet-result-pattern
Implements the Result pattern for explicit error handling without exceptions. Provides Result, Result<T>, and Error types for clean, predictable control flow in domain-driven applications.
git clone --depth 1 https://github.com/ronnythedev/dotnet-clean-architecture-skills /tmp/dotnet-result-pattern && cp -r /tmp/dotnet-result-pattern/skills/08-dotnet-result-pattern ~/.claude/skills/dotnet-result-patternSKILL.md
# Result Pattern Implementation
## Overview
The Result pattern provides explicit error handling without exceptions:
- **No exceptions for business errors** - Exceptions for truly exceptional cases only
- **Explicit success/failure** - Compiler forces handling of both cases
- **Composable errors** - Chain operations, fail fast
- **Self-documenting** - Method signatures show possible outcomes
## Quick Reference
| Type | Purpose | Usage |
|------|---------|-------|
| `Result` | Operation without return value | Update, Delete operations |
| `Result<T>` | Operation with return value | Create, Get operations |
| `Error` | Error information | Code + Description |
---
## Implementation Structure
```
/Domain/Abstractions/
├── Result.cs # Result and Result<T>
├── Error.cs # Error record
└── ValidationResult.cs # Multiple errors support
```
---
## Template: Core Result Types
```csharp
// src/{name}.domain/Abstractions/Error.cs
namespace {name}.domain.abstractions;
/// <summary>
/// Represents an error with a code and description
/// </summary>
public record Error(string Code, string Description)
{
/// <summary>
/// Represents no error (success state)
/// </summary>
public static readonly Error None = new(string.Empty, string.Empty);
/// <summary>
/// Represents a null value error
/// </summary>
public static readonly Error NullValue = new(
"Error.NullValue",
"A null value was provided");
/// <summary>
/// Creates an error from an exception
/// </summary>
public static Error FromException(Exception exception) => new(
"Error.Exception",
exception.Message);
/// <summary>
/// Implicit conversion to string (returns Code)
/// </summary>
public static implicit operator string(Error error) => error.Code;
public override string ToString() => Code;
}
```
```csharp
// src/{name}.domain/Abstractions/Result.cs
namespace {name}.domain.abstractions;
/// <summary>
/// Represents the outcome of an operation that doesn't return a value
/// </summary>
public class Result
{
protected Result(bool isSuccess, Error error)
{
if (isSuccess && error != Error.None)
{
throw new InvalidOperationException(
"Cannot create successful result with an error");
}
if (!isSuccess && error == Error.None)
{
throw new InvalidOperationException(
"Cannot create failed result without an error");
}
IsSuccess = isSuccess;
Error = error;
}
public bool IsSuccess { get; }
public bool IsFailure => !IsSuccess;
public Error Error { get; }
// ═══════════════════════════════════════════════════════════════
// FACTORY METHODS
// ═══════════════════════════════════════════════════════════════
/// <summary>
/// Creates a successful result
/// </summary>
public static Result Success() => new(true, Error.None);
/// <summary>
/// Creates a failed result with the specified error
/// </summary>
public static Result Failure(Error error) => new(false, error);
/// <summary>
/// Creates a successful result with a value
/// </summary>
public static Result<TValue> Success<TValue>(TValue value) =>
new(value, true, Error.None);
/// <summary>
/// Creates a failed result with the specified error
/// </summary>
public static Result<TValue> Failure<TValue>(Error error) =>
new(default, false, error);
/// <summary>
/// Creates a result based on a condition
/// </summary>
public static Result Create(bool condition, Error error) =>
condition ? Success() : Failure(error);
/// <summary>
/// Creates a result based on a condition with a value
/// </summary>
public static Result<TValue> Create<TValue>(TValue? value, Error error) =>
value is not null ? Success(value) : Failure<TValue>(error);
}
/// <summary>
/// Represents the outcome of an operation that returns a value
/// </summary>
public class Result<TValue> : Result
{
private readonly TValue? _value;
protected internal Result(TValue? value, bool isSuccess, Error error)
: base(isSuccess, error)
{
_value = value;
}
/// <summary>
/// Gets the value if successful, throws if failed
/// </summary>
public TValue Value => IsSuccess
? _value!
: throw new InvalidOperationException(
$"Cannot access value of a failed result. Error: {Error.Code}");
/// <summary>
/// Implicit conversion from value to successful Result
/// </summary>
public static implicit operator Result<TValue>(TValue? value) =>
value is not null ? Success(value) : Failure<TValue>(Error.NullValue);
/// <summary>
/// Implicit conversion from Error to failed Result
/// </summary>
public static implicit operator Result<TValue>(Error error) =>
Failure<TValue>(error);
}
```
---
## Template: Result Extensions (Functional Operations)
```csharp
// src/{name}.domain/Abstractions/ResultExtensions.cs
namespace {name}.domain.abstractions;
public static class ResultExtensions
{
// ═══════════════════════════════════════════════════════════════
// MAP: Transform success value
// ═══════════════════════════════════════════════════════════════
/// <summary>
/// Transforms the value if successful, preserves error if failed
/// </summary>
public static Result<TOut> Map<TIn, TOut>(
this Result<TIn> result,
Func<TIn, TOut> mapper)
{
return result.IsSuccess
? Result.Success(mapper(result.Value))
: Result.Failure<TOut>(result.Error);
}
/// <summary>
/// Async version of Map
/// </summary>
public static async Task<Result<TOut>> Map<TIn, TOut>(
this Task<Result<TIn>> resultTask,
Func<TIn, TOut> mapper)
{
varScaffolds a complete .NET solution following Clean Architecture principles with proper layer separation (API, Application, Domain, Infrastructure). Creates project structure, dependency injection setup, and cross-cutting concerns configuration.
Generates CQRS Commands with Handlers, Validators, and Request DTOs following Clean Architecture patterns. Commands represent actions that modify state and return Result types for proper error handling.
Generates CQRS Queries with Handlers and Response DTOs for read operations. Uses Dapper for optimized read queries, bypassing the domain model for better performance.
Generates Domain Entities following DDD principles with factory methods, private setters, domain events, and proper encapsulation. Supports aggregate roots, child entities, and value objects.
Generates Repository interfaces and implementations following the Repository pattern. Provides data access abstraction for aggregate roots with EF Core implementations.
Generates Entity Framework Core configurations using Fluent API. Maps domain entities to database tables with proper relationships, constraints, and conventions.
Generates RESTful API Controllers with proper routing, versioning, authorization, and MediatR integration. Follows REST conventions and Clean Architecture patterns.
Generates Minimal API endpoints following Microsoft's recommended approach. Creates fast, testable HTTP APIs with minimal code using MapGet/MapPost/MapPut/MapDelete. Preferred over controller-based APIs for new projects.