A lightweight traceability library for building robust error handling in .NET applications using FluentResults patterns.
$ dotnet add package Baubit.TraceabilityA lightweight .NET library for building robust error handling and result tracing in .NET applications using the FluentResults pattern.
Baubit.Traceability provides a comprehensive set of abstractions and extension methods for working with operation results, errors, and success states. It extends the popular FluentResults library with additional traceability features, making it easier to track, understand, and debug complex operation chains in your applications.
dotnet add package Baubit.Traceability
using Baubit.Traceability;
using FluentResults;
public class UserService
{
public Result<User> GetUser(int id)
{
var result = ValidateUserId(id);
if (result.IsFailed)
{
return result.ToResult<User>()
.AddReasonIfFailed(new UserNotFoundReason(id));
}
var user = FetchUserFromDatabase(id);
return Result.Ok(user);
}
}
using Baubit.Traceability.Errors;
using Baubit.Traceability.Reasons;
public class UserNotFoundError : AError
{
public UserNotFoundError(int userId)
: base([], $"User with ID {userId} was not found", new Dictionary<string, object>
{
{ "UserId", userId },
{ "Timestamp", DateTime.UtcNow }
})
{
}
}
public class UserNotFoundReason : AReason
{
public UserNotFoundReason(int userId)
: base($"User {userId} does not exist in the database", new Dictionary<string, object>
{
{ "UserId", userId }
})
{
}
}
using Baubit.Traceability;
using FluentResults;
// Throw exception on failure
var result = PerformOperation()
.ThrowIfFailed();
// Add success markers
var successResult = Result.Ok(data)
.AddSuccessIfPassed(new OperationSuccess("Data loaded successfully"));
// Unwrap nested reasons
var reasons = complexResult.UnwrapReasons();
// Dispose resources safely
var disposables = new List<IDisposable> { resource1, resource2 };
var disposeResult = disposables.Dispose();
using Baubit.Traceability.Successes;
public class DataSavedSuccess : ASuccess
{
public DataSavedSuccess(string entityId)
: base($"Data saved successfully for entity {entityId}", new Dictionary<string, object>
{
{ "EntityId", entityId },
{ "SavedAt", DateTime.UtcNow }
})
{
}
}
public Result SaveData(Data data)
{
// Save logic...
return Result.Ok()
.AddSuccessIfPassed(new DataSavedSuccess(data.Id));
}
AErrorAbstract base class for custom error types. Provides:
Message - Error messageReasons - List of nested error reasonsMetadata - Additional context as key-value pairsCreationTime - Timestamp when error was createdAReasonAbstract base class for custom reason types. Provides:
Message - Reason descriptionMetadata - Additional context as key-value pairsCreationTime - Timestamp when reason was createdASuccessAbstract base class for custom success types. Inherits from AReason and implements ISuccess.
ThrowIfFailed<TResult>() - Throws FailedOperationException if result is failedThrowIfFailed<TResult>(Task<TResult>) - Async version of ThrowIfFailedAddSuccessIfPassed<TResult>(params ISuccess[]) - Adds success markers when result succeedsAddReasonIfFailed(params IReason[]) - Adds reasons when result failsAddErrorIfFailed<TError>(params TError[]) - Adds errors when result failsUnwrapReasons<TResult>() - Recursively unwraps all reasons including nested exceptionsGetNonErrors<TResult>() - Returns only non-error reasonsDispose<TDisposable>(IList<TDisposable>) - Safely disposes a collection of disposable objectsFailedOperationExceptionException thrown by ThrowIfFailed() when a result has failed. Contains the original IResultBase result.
try
{
var result = PerformComplexOperation()
.ThrowIfFailed();
}
catch (FailedOperationException ex)
{
var allReasons = ex.Result.UnwrapReasons();
// Process all reasons including those from nested exceptions
}
var result = Result.Ok(data);
if (shouldLogSuccess)
{
result = result.AddSuccessIfPassed(
(r, successes) => r.WithSuccesses(successes),
new OperationSuccess("Operation completed")
);
}
var resources = new List<IDisposable>
{
connection,
transaction,
reader
};
var cleanupResult = resources.Dispose();
if (cleanupResult.IsFailed)
{
logger.LogError("Failed to dispose resources", cleanupResult.Errors);
}
# Clone the repository
git clone https://github.com/pnagoorkar/Baubit.Traceability.git
cd Baubit.Traceability
# Build the solution
dotnet build
# Run tests
dotnet test
# Run tests with coverage
dotnet test --collect:"XPlat Code Coverage"
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE file for details.
See ACKNOWLEDGEMENT.md for details on libraries and ideas that support this project.
Copyright © 2025 Prashant Nagoorkar