Skip to content

Reefact/first-class-errors

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

670 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

FirstClassErrors

🌍 Languages:
πŸ‡¬πŸ‡§ English (this file) | πŸ‡«πŸ‡· FranΓ§ais

Build ci
Quality Quality Gate Coverage
Security codeql OpenSSF Best Practices OpenSSF Scorecard
Package NuGet .NET Standard 2.0
Project License Conventional Commits

Turn your errors into structured, living knowledge about your system.

FirstClassErrors

🧩 One model for the whole chain

An application error too often boils down to a bare code or string, with nothing that explains what it actually means. FirstClassErrors turns it into a first-class concept of your domain. You describe each error situation once, with its rules, in one place in the code; the whole library then builds on that model.

Model the error An error becomes an object that carries its own meaning β€” a violated business rule, a rejected incoming request, or a failing external service, transient or not.

Handle the failure You choose how it travels: thrown as an ordinary exception, or returned as an explicit result the caller inspects without a try/catch.

Document the error Each error can carry its explanation, its rule, and its likely causes right there in the code. FirstClassErrors generates a human-readable catalog from that same code β€” so it never drifts.

Check the model Roslyn analyzers enforce these rules at compile time: a duplicated error code or inconsistent documentation becomes a build error, caught before it ever reaches production.

Bind external input A form or a JSON request must become a valid domain object. Where classic .NET binding restates those rules separately, in a validator or attributes, FirstClassErrors binds that input directly to your value objects, reusing their own construction rules instead of duplicating them.

🚨 The problem

A production error is rarely useful as only a type and a string:

Invalid operation.

Developers and support still need to discover:

  • which situation actually occurred;
  • which rule was violated;
  • which facts belong to this occurrence;
  • what might have caused it;
  • where to start investigating.

When that knowledge lives in logs, tickets, comments, and people's memories, it drifts away from the code.

πŸ’‘ The FirstClassErrors approach

A factory gives the error situation a stable identity, keeps its construction in one place, and attaches structured documentation that will feed a generated, human-readable catalog:

[ProvidesErrorsFor(nameof(Amount))]
public static class InvalidAmountOperationError {

    [DocumentedBy(nameof(CurrencyMismatchDocumentation))]
    internal static DomainError CurrencyMismatch(Amount left, Amount right) {
        return DomainError.Create(
                ErrorCode.Create("AMOUNT_CURRENCY_MISMATCH"),
                diagnosticMessage: $"Cannot add {left} and {right} because their currencies differ.")
            .WithPublicMessage(
                shortMessage: "The amounts use different currencies.",
                detailedMessage: "Both amounts must use the same currency.");
    }

    private static ErrorDocumentation CurrencyMismatchDocumentation() {
        return DescribeError.WithTitle("Amount currency mismatch")
                            .WithDescription("This error occurs when an operation combines amounts expressed in different currencies.")
                            .WithRule("A monetary operation must use one common currency.")
                            .WithDiagnostic(
                                "The amounts reached the operation without being converted to one currency.",
                                ErrorOrigin.Internal,
                                "Verify where the amounts should have been converted before this operation.")
                            .WithExamples(() => CurrencyMismatch(
                                new Amount(10, Currency.EUR),
                                new Amount(12, Currency.USD)));
    }
}

The domain code stays focused on intent:

if (Currency != other.Currency) {
    throw InvalidAmountOperationError.CurrencyMismatch(this, other).ToException();
}

The factory returns an Error (DomainError is one of its categories), so expected failures can use the same model without throwing:

return Outcome<Amount>.Failure(
    InvalidAmountOperationError.CurrencyMismatch(left, right));

From these factories, FirstClassErrors can generate a human-readable catalog for developers, support teams, and operations.

πŸ“¦ Installation

dotnet add package FirstClassErrors

The package targets .NET Standard 2.0. Its Roslyn analyzers are bundled automatically; no separate analyzer package is required.

To bind incoming requests into typed commands or queries at the primary-adapter boundary, add the optional request binder:

dotnet add package FirstClassErrors.RequestBinder

To generate documentation, install the CLI:

dotnet tool install --global FirstClassErrors.Cli

Then follow the Getting Started guide to create and generate your first documented error.

🎯 When it is a good fit

FirstClassErrors is especially useful for long-lived application or domain code where:

  • errors represent rules, constraints, or boundary failures;
  • several teams or systems depend on stable error codes;
  • support and operations investigate production failures;
  • documentation must remain aligned with behavior.

For prototypes, tiny utilities, or low-level technical code, standard exceptions may be enough. See When Not to Use FirstClassErrors.

πŸ” Analyzers and supply-chain information

The package includes Roslyn rules with stable FCExxx identifiers. They detect duplicate or malformed error codes, invalid documentation wiring, missing examples, and common API misuse. See the analyzer rules reference.

Released packages include signed build provenance and an embedded SPDX SBOM. See the release and verification details in the supply-chain documentation.

πŸ› Feedback and contributing

Found a bug or want to request a feature? Open an issue on the GitHub issue tracker. Contributions are welcome; see CONTRIBUTING.md.

For security vulnerabilities, follow the private process in SECURITY.md.

πŸ“š Documentation

Discover

Understand the model

Write and use errors

Generate and operate the catalog

Evaluate and troubleshoot

About

Tiny .NET library for structured, diagnosable exceptions.

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors