-
Notifications
You must be signed in to change notification settings - Fork 0
Full Example
ferdi kurnaz edited this page Aug 27, 2026
·
1 revision
This page walks through a complete, realistic example, based on the Resultron.Sample console app included in the repository. It's a simple "user management" system: creating, fetching, updating, and deleting users.
public record User(Guid Id, string Name, string Email, DateTime CreatedAt);public static class UserErrors
{
public static Error NotFound(Guid id) =>
new("User.NotFound", $"User with id '{id}' was not found.");
public static Error EmailAlreadyExists(string email) =>
new("User.EmailAlreadyExists", $"A user with email '{email}' already exists.");
public static Error InvalidName =>
new("User.InvalidName", "Name must be at least 2 characters.");
public static Error InvalidEmail =>
new("User.InvalidEmail", "Email must contain '@'.");
}Notice how each error has a clear Code (like "User.NotFound") and a friendly Description. This makes debugging and logging much easier.
public interface IUserRepository
{
Result<User> GetById(Guid id);
Result<List<User>> GetAll();
Result<User> Add(User user);
Result<User> Update(User user);
Result Delete(Guid id);
bool EmailExists(string email);
}Notice that almost every method returns a Result or Result<T> — this keeps the "success or failure" idea consistent throughout the whole app.
public sealed class UserService
{
private readonly IUserRepository _repository;
public UserService(IUserRepository repository) => _repository = repository;
public Result<User> Create(string name, string email)
{
return ValidateName(name)
.Bind(() => ValidateEmail(email))
.Bind(() => CheckEmailUniqueness(email))
.Bind(() => SaveUser(name, email));
}
public Result<User> GetById(Guid id) => _repository.GetById(id);
public Result<List<User>> GetAll() => _repository.GetAll();
public Result<User> UpdateName(Guid id, string newName)
{
return ValidateName(newName)
.Bind(() => _repository.GetById(id))
.Map(user => new User(user.Id, newName, user.Email, user.CreatedAt))
.Bind(_repository.Update);
}
public Result Delete(Guid id)
{
return _repository.GetById(id)
.Bind(user => _repository.Delete(user.Id));
}
private static Result ValidateName(string name) =>
name.Length >= 2 ? Result.Success() : Result.Failure(UserErrors.InvalidName);
private static Result ValidateEmail(string email) =>
email.Contains('@') ? Result.Success() : Result.Failure(UserErrors.InvalidEmail);
private Result CheckEmailUniqueness(string email) =>
_repository.EmailExists(email)
? Result.Failure(UserErrors.EmailAlreadyExists(email))
: Result.Success();
private Result<User> SaveUser(string name, string email)
{
var user = new User(Guid.NewGuid(), name, email, DateTime.UtcNow);
return _repository.Add(user);
}
}var repository = new InMemoryUserRepository();
var service = new UserService(repository);
// Successful creation
var createResult = service.Create("Arda Terekeci", "arda@example.com");
createResult.Match(
onSuccess: user => Console.WriteLine($"[OK] User created: {user.Id} - {user.Name}"),
onFailure: error => Console.WriteLine($"[ERROR] {error.Code}: {error.Description}")
);
// Trying to create a user with a duplicate email
var duplicateResult = service.Create("Someone Else", "arda@example.com");
duplicateResult.Match(
onSuccess: user => Console.WriteLine($"[OK] {user.Name}"),
onFailure: error => Console.WriteLine($"[ERROR] {error.Code}: {error.Description}")
);
// Output: [ERROR] User.EmailAlreadyExists: A user with email 'arda@example.com' already exists.
// Trying to create a user with an invalid name
var invalidResult = service.Create("A", "a@example.com");
invalidResult.Match(
onSuccess: user => Console.WriteLine($"[OK] {user.Name}"),
onFailure: error => Console.WriteLine($"[ERROR] {error.Code}: {error.Description}")
);
// Output: [ERROR] User.InvalidName: Name must be at least 2 characters.-
Validation as small, reusable steps (
ValidateName,ValidateEmail) that each return aResult. -
Chaining with
Bindso the whole "Create" operation reads top-to-bottom, like a checklist. -
Mapused for the simple, "cannot fail" transformation of building an updatedUserobject. -
Consistent error codes (
User.InvalidName,User.NotFound, etc.) that make it easy to know exactly what happened, and could be mapped to specific HTTP status codes in a web API (for example,NotFound→ HTTP 404,InvalidName/InvalidEmail→ HTTP 400). -
Matchused at the very end, as the single place where the app decides what to print (or, in a real app, what HTTP response to send).
The full sample project is available in the repository at
sample/Resultron.Sample.
Next: check the FAQ for common questions.