Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
glen-84 opened this issue Dec 10, 2023 · 0 comments

Comments

@glen-84
Copy link

glen-84 commented Dec 10, 2023

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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant