Skip to content

Pass a private object implementing a public interface representing the unique value to the entity method #12

@glen-84

Description

@glen-84

Example

Entity method:

public static User Register(
    UserId id,
    IUniqueUsername uniqueUsername,
    IUniqueEmailAddress uniqueEmailAddress)
{ /* ... */ }

Public interfaces:

public interface IUniqueUsername
{
    public string Value { get; }
}

public interface IUniqueEmailAddress
{
    public string Value { get; }
}

Domain service:

public sealed class UserService(IUsersUnitOfWork unitOfWork)
{
    private readonly IUsersUnitOfWork unitOfWork = unitOfWork;

    public async Task<Result<IUniqueEmailAddress>> EnsureUniqueEmailAddressAsync(
        EmailAddress emailAddress,
        UserId? existingUserId = null)
    {
        var isUnique = await this.unitOfWork.Users.IsUniqueAsync(
            new UserByEmailAddressSpec(emailAddress),
            existingUserId);

        if (isUnique)
        {
            return new UniqueEmailAddress(emailAddress.Value);
        }

        return new Error($"A user with the email address '{emailAddress}' already exists.");
    }

    public async Task<Result<IUniqueUsername>> EnsureUniqueUsernameAsync(
        Username username,
        UserId? existingUserId = null)
    {
        var isUnique = await this.unitOfWork.Users.IsUniqueAsync(
            new UserByUsernameSpec(username),
            existingUserId);

        if (isUnique)
        {
            return new UniqueUsername(username.Value);
        }

        return new Error($"A user with the username '{username}' already exists.");
    }

    private sealed record UniqueEmailAddress(string Value) : IUniqueEmailAddress;
    private sealed record UniqueUsername(string Value) : IUniqueUsername;
}

Usage in handler:

public async Task<Result<long>> Handle(
    RegisterUserCommand command,
    CancellationToken cancellationToken = default)
{
    var uniqueUsernameResult = await this.userService.EnsureUniqueUsernameAsync(
        Username.From(command.Username));

    var uniqueEmailAddressResult = await this.userService.EnsureUniqueEmailAddressAsync(
        EmailAddress.From(command.EmailAddress));

    var result = Result.Combine(uniqueUsernameResult, uniqueEmailAddressResult);

    if (result.IsFailure)
    {
        return result.Errors;
    }

    var user = User.Register(
        UserId.From(this.idGenerator.CreateId()),
        uniqueUsernameResult.Value,
        uniqueEmailAddressResult.Value);

    this.unitOfWork.Users.Add(user);

    await this.unitOfWork.SaveChangesAsync(cancellationToken);

    return user.Id;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions