Receber por email
← Voltar para o catálogo

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.

66estrelas
Atualizado há 5 meses

Ver no GitHub ↗

Como adicionar

/plugin marketplace add ronnythedev/dotnet-clean-architecture-skills

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/dotnet-result-pattern/svg)](https://www.skillteca.com.br/skills/dotnet-result-pattern)

Skills relacionadas

Ver todas de Desenvolvimento →

Alerta por categoria

Receba novas skills de Desenvolvimento toda segunda

1 email curto, só com as skills novas de Desenvolvimento. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

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

// 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;
}
// 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)

// 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)
    {
        var result = await resultTask;
        return result.Map(mapper);
    }

    // ═══════════════════════════════════════════════════════════════
    // BIND: Chain operations that return Result
    // ═══════════════════════════════════════════════════════════════

    /// <summary>
    /// Chains another Result-returning operation if successful
    /// </summary>
    public static Result<TOut> Bind<TIn, TOut>(
        this Result<TIn> result,
        Func<TIn, Result<TOut>> binder)
    {
        return result.IsSuccess
            ? binder(result.Value)
            : Result.Failure<TOut>(result.Error);
    }

    /// <summary>
    /// Async version of Bind
    /// </summary>
    public static async Task<Result<TOut>> Bind<TIn, TOut>(
        this Result<TIn> result,
        Func<TIn, Task<Result<TOut>>> binder)
    {
        return result.IsSuccess
            ? await binder(result.Value)
            : Result.Failure<TOut>(result.Error);
    }

    /// <summary>
    /// Async version of Bind for Task results
    /// </summary>
    public static async Task<Result<TOut>> Bind<TIn, TOut>(
        this Task<Result<TIn>> resultTask,
        Func<TIn, Result<TOut>> binder)
    {
        var result = await resultTask;
        return result.Bind(binder);
    }

    /// <summary>
    /// Fully async Bind
    /// </summary>
    public static async Task<Result<TOut>> Bind<TIn, TOut>(
        this Task<Result<TIn>> resultTask,
        Func<TIn, Task<Result<TOut>>> binder)
    {
        var result = await resultTask;
        return await result.Bind(binder);
    }

    // ═══════════════════════════════════════════════════════════════
    // TAP: Execute side effect without changing result
    // ═══════════════════════════════════════════════════════════════

    /// <summary>
    /// Executes an action if successful, returns original result
    /// </summary>
    public static Result<T> Tap<T>(
        this Result<T> result,
        Action<T> action)
    {
        if (result.IsSucc
Compartilhar X LinkedIn