mirror of
https://github.com/alex289/CleanArchitecture.git
synced 2025-08-25 04:45:29 +00:00
Add domain tests
This commit is contained in:
parent
305e6320f0
commit
e937e786a7
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Application.Interfaces;
|
using CleanArchitecture.Application.Interfaces;
|
||||||
|
using CleanArchitecture.Application.ViewModels.Users;
|
||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@ -35,20 +36,23 @@ public class UserController : ApiController
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public string CreateUserAsync()
|
public async Task<IActionResult> CreateUserAsync([FromBody] CreateUserViewModel viewModel)
|
||||||
{
|
{
|
||||||
return "test";
|
await _userService.CreateUserAsync(viewModel);
|
||||||
|
return Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
public string DeleteUserAsync([FromRoute] Guid id)
|
public async Task<IActionResult> DeleteUserAsync([FromRoute] Guid id)
|
||||||
{
|
{
|
||||||
return "test";
|
await _userService.DeleteUserAsync(id);
|
||||||
|
return Response(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut]
|
[HttpPut]
|
||||||
public string UpdateUserAsync()
|
public async Task<IActionResult> UpdateUserAsync([FromBody] UpdateUserViewModel viewModel)
|
||||||
{
|
{
|
||||||
return "test";
|
await _userService.UpdateUserAsync(viewModel);
|
||||||
|
return Response(viewModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,6 +19,6 @@ public sealed class GetAllUsersQueryHandlerTests
|
|||||||
|
|
||||||
result.Should().NotBeNull();
|
result.Should().NotBeNull();
|
||||||
result.Should().ContainSingle();
|
result.Should().ContainSingle();
|
||||||
result.FirstOrDefault().Id.Should().Be(_fixture.ExistingUserId);
|
result.FirstOrDefault()!.Id.Should().Be(_fixture.ExistingUserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ using CleanArchitecture.Application.Queries.Users.GetAll;
|
|||||||
using CleanArchitecture.Application.Queries.Users.GetUserById;
|
using CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||||
using CleanArchitecture.Application.Services;
|
using CleanArchitecture.Application.Services;
|
||||||
using CleanArchitecture.Application.ViewModels;
|
using CleanArchitecture.Application.ViewModels;
|
||||||
|
using CleanArchitecture.Application.ViewModels.Users;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using CleanArchitecture.Application.ViewModels;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using CleanArchitecture.Application.ViewModels.Users;
|
||||||
|
|
||||||
namespace CleanArchitecture.Application.Interfaces;
|
namespace CleanArchitecture.Application.Interfaces;
|
||||||
|
|
||||||
@ -9,4 +9,7 @@ public interface IUserService
|
|||||||
{
|
{
|
||||||
public Task<UserViewModel?> GetUserByUserIdAsync(Guid userId);
|
public Task<UserViewModel?> GetUserByUserIdAsync(Guid userId);
|
||||||
public Task<IEnumerable<UserViewModel>> GetAllUsersAsync();
|
public Task<IEnumerable<UserViewModel>> GetAllUsersAsync();
|
||||||
|
public Task CreateUserAsync(CreateUserViewModel user);
|
||||||
|
public Task UpdateUserAsync(UpdateUserViewModel user);
|
||||||
|
public Task DeleteUserAsync(Guid userId);
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using CleanArchitecture.Application.ViewModels;
|
using CleanArchitecture.Application.ViewModels;
|
||||||
|
using CleanArchitecture.Application.ViewModels.Users;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace CleanArchitecture.Application.Queries.Users.GetAll;
|
namespace CleanArchitecture.Application.Queries.Users.GetAll;
|
||||||
|
@ -3,6 +3,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Application.ViewModels;
|
using CleanArchitecture.Application.ViewModels;
|
||||||
|
using CleanArchitecture.Application.ViewModels.Users;
|
||||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Application.ViewModels;
|
using CleanArchitecture.Application.ViewModels;
|
||||||
|
using CleanArchitecture.Application.ViewModels.Users;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace CleanArchitecture.Application.Queries.Users.GetUserById;
|
namespace CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Application.ViewModels;
|
using CleanArchitecture.Application.ViewModels;
|
||||||
|
using CleanArchitecture.Application.ViewModels.Users;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
|
@ -4,7 +4,10 @@ using System.Threading.Tasks;
|
|||||||
using CleanArchitecture.Application.Interfaces;
|
using CleanArchitecture.Application.Interfaces;
|
||||||
using CleanArchitecture.Application.Queries.Users.GetAll;
|
using CleanArchitecture.Application.Queries.Users.GetAll;
|
||||||
using CleanArchitecture.Application.Queries.Users.GetUserById;
|
using CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||||
using CleanArchitecture.Application.ViewModels;
|
using CleanArchitecture.Application.ViewModels.Users;
|
||||||
|
using CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||||
|
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||||
|
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
|
|
||||||
namespace CleanArchitecture.Application.Services;
|
namespace CleanArchitecture.Application.Services;
|
||||||
@ -27,4 +30,27 @@ public sealed class UserService : IUserService
|
|||||||
{
|
{
|
||||||
return await _bus.QueryAsync(new GetAllUsersQuery());
|
return await _bus.QueryAsync(new GetAllUsersQuery());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task CreateUserAsync(CreateUserViewModel user)
|
||||||
|
{
|
||||||
|
await _bus.SendCommandAsync(new CreateUserCommand(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
user.Email,
|
||||||
|
user.Surname,
|
||||||
|
user.GivenName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateUserAsync(UpdateUserViewModel user)
|
||||||
|
{
|
||||||
|
await _bus.SendCommandAsync(new UpdateUserCommand(
|
||||||
|
user.Id,
|
||||||
|
user.Email,
|
||||||
|
user.Surname,
|
||||||
|
user.GivenName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteUserAsync(Guid userId)
|
||||||
|
{
|
||||||
|
await _bus.SendCommandAsync(new DeleteUserCommand(userId));
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace CleanArchitecture.Application.ViewModels.Users;
|
||||||
|
|
||||||
|
public sealed record CreateUserViewModel(
|
||||||
|
string Email,
|
||||||
|
string Surname,
|
||||||
|
string GivenName);
|
@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Application.ViewModels.Users;
|
||||||
|
|
||||||
|
public sealed record UpdateUserViewModel(
|
||||||
|
Guid Id,
|
||||||
|
string Email,
|
||||||
|
string Surname,
|
||||||
|
string GivenName);
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.Entities;
|
using CleanArchitecture.Domain.Entities;
|
||||||
|
|
||||||
namespace CleanArchitecture.Application.ViewModels;
|
namespace CleanArchitecture.Application.ViewModels.Users;
|
||||||
|
|
||||||
public sealed class UserViewModel
|
public sealed class UserViewModel
|
||||||
{
|
{
|
@ -0,0 +1,52 @@
|
|||||||
|
using CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||||
|
using CleanArchitecture.Domain.Errors;
|
||||||
|
using CleanArchitecture.Domain.Events.User;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.CreateUser;
|
||||||
|
|
||||||
|
public sealed class CreateUserCommandHandlerTests
|
||||||
|
{
|
||||||
|
private readonly CreateUserCommandTestFixture _fixture = new();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Create_User()
|
||||||
|
{
|
||||||
|
_fixture.SetupUser();
|
||||||
|
|
||||||
|
var command = new CreateUserCommand(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
"test@email.com",
|
||||||
|
"Test",
|
||||||
|
"Email");
|
||||||
|
|
||||||
|
_fixture.CommandHandler.Handle(command, default).Wait();
|
||||||
|
|
||||||
|
_fixture
|
||||||
|
.VerifyNoDomainNotification()
|
||||||
|
.VerifyCommit()
|
||||||
|
.VerifyRaisedEvent<UserCreatedEvent>(x => x.UserId == command.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Not_Create_Already_Existing_User()
|
||||||
|
{
|
||||||
|
var user = _fixture.SetupUser();
|
||||||
|
|
||||||
|
var command = new CreateUserCommand(
|
||||||
|
user.Id,
|
||||||
|
"test@email.com",
|
||||||
|
"Test",
|
||||||
|
"Email");
|
||||||
|
|
||||||
|
_fixture.CommandHandler.Handle(command, default).Wait();
|
||||||
|
|
||||||
|
_fixture
|
||||||
|
.VerifyNoCommit()
|
||||||
|
.VerifyNoRaisedEvent<UserCreatedEvent>()
|
||||||
|
.VerifyAnyDomainNotification()
|
||||||
|
.VerifyExistingNotification(
|
||||||
|
DomainErrorCodes.UserAlreadyExists,
|
||||||
|
$"There is already a User with Id {command.UserId}");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
using CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||||
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.CreateUser;
|
||||||
|
|
||||||
|
public sealed class CreateUserCommandTestFixture : CommandHandlerFixtureBase
|
||||||
|
{
|
||||||
|
public CreateUserCommandHandler CommandHandler { get; }
|
||||||
|
private Mock<IUserRepository> UserRepository { get; }
|
||||||
|
|
||||||
|
public CreateUserCommandTestFixture()
|
||||||
|
{
|
||||||
|
UserRepository = new Mock<IUserRepository>();
|
||||||
|
|
||||||
|
CommandHandler = new(
|
||||||
|
Bus.Object,
|
||||||
|
UnitOfWork.Object,
|
||||||
|
NotificationHandler.Object,
|
||||||
|
UserRepository.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entities.User SetupUser()
|
||||||
|
{
|
||||||
|
var user = new Entities.User(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
"max@mustermann.com",
|
||||||
|
"Max",
|
||||||
|
"Mustermann");
|
||||||
|
|
||||||
|
UserRepository
|
||||||
|
.Setup(x => x.GetByIdAsync(It.Is<Guid>(y => y == user.Id)))
|
||||||
|
.ReturnsAsync(user);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
using CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||||
|
using CleanArchitecture.Domain.Errors;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.CreateUser;
|
||||||
|
|
||||||
|
public sealed class CreateUserCommandValidationTests :
|
||||||
|
ValidationTestBase<CreateUserCommand, CreateUserCommandValidation>
|
||||||
|
{
|
||||||
|
public CreateUserCommandValidationTests() : base(new CreateUserCommandValidation())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Valid()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand();
|
||||||
|
|
||||||
|
ShouldBeValid(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Empty_User_Id()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(userId: Guid.Empty);
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserEmptyId,
|
||||||
|
"User id may not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Empty_Email()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(email: string.Empty);
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserInvalidEmail,
|
||||||
|
"Email is not a valid email address");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Invalid_Email()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(email: "not a email");
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserInvalidEmail,
|
||||||
|
"Email is not a valid email address");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Email_Exceeds_Max_Length()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(email: new string('a', 320) + "@test.com");
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserEmailExceedsMaxLength,
|
||||||
|
"Email may not be longer than 320 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Empty_Surname()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(surName: "");
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserEmptySurname,
|
||||||
|
"Surname may not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Surname_Exceeds_Max_Length()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(surName: new string('a', 101));
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserSurnameExceedsMaxLength,
|
||||||
|
"Surname may not be longer than 100 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Empty_Given_Name()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(givenName: "");
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserEmptyGivenName,
|
||||||
|
"Given name may not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Given_Name_Exceeds_Max_Length()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(givenName: new string('a', 101));
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserGivenNameExceedsMaxLength,
|
||||||
|
"Given name may not be longer than 100 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
private CreateUserCommand CreateTestCommand(
|
||||||
|
Guid? userId = null,
|
||||||
|
string? email = null,
|
||||||
|
string? surName = null,
|
||||||
|
string? givenName = null) =>
|
||||||
|
new (
|
||||||
|
userId ?? Guid.NewGuid(),
|
||||||
|
email ?? "test@email.com",
|
||||||
|
surName ?? "test",
|
||||||
|
givenName ?? "email");
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||||
|
using CleanArchitecture.Domain.Errors;
|
||||||
|
using CleanArchitecture.Domain.Events.User;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.DeleteUser;
|
||||||
|
|
||||||
|
public sealed class DeleteUserCommandHandlerTests
|
||||||
|
{
|
||||||
|
private readonly DeleteUserCommandTestFixture _fixture = new();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Delete_User()
|
||||||
|
{
|
||||||
|
var user = _fixture.SetupUser();
|
||||||
|
|
||||||
|
var command = new DeleteUserCommand(user.Id);
|
||||||
|
|
||||||
|
_fixture.CommandHandler.Handle(command, default).Wait();
|
||||||
|
|
||||||
|
_fixture
|
||||||
|
.VerifyNoDomainNotification()
|
||||||
|
.VerifyCommit()
|
||||||
|
.VerifyRaisedEvent<UserDeletedEvent>(x => x.UserId == user.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Not_Delete_Non_Existing_User()
|
||||||
|
{
|
||||||
|
_fixture.SetupUser();
|
||||||
|
|
||||||
|
var command = new DeleteUserCommand(Guid.NewGuid());
|
||||||
|
|
||||||
|
_fixture.CommandHandler.Handle(command, default).Wait();
|
||||||
|
|
||||||
|
_fixture
|
||||||
|
.VerifyNoCommit()
|
||||||
|
.VerifyNoRaisedEvent<UserDeletedEvent>()
|
||||||
|
.VerifyAnyDomainNotification()
|
||||||
|
.VerifyExistingNotification(
|
||||||
|
ErrorCodes.ObjectNotFound,
|
||||||
|
$"There is no User with Id {command.UserId}");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||||
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.DeleteUser;
|
||||||
|
|
||||||
|
public sealed class DeleteUserCommandTestFixture : CommandHandlerFixtureBase
|
||||||
|
{
|
||||||
|
public DeleteUserCommandHandler CommandHandler { get; }
|
||||||
|
private Mock<IUserRepository> UserRepository { get; }
|
||||||
|
|
||||||
|
public DeleteUserCommandTestFixture()
|
||||||
|
{
|
||||||
|
UserRepository = new Mock<IUserRepository>();
|
||||||
|
|
||||||
|
CommandHandler = new (
|
||||||
|
Bus.Object,
|
||||||
|
UnitOfWork.Object,
|
||||||
|
NotificationHandler.Object,
|
||||||
|
UserRepository.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entities.User SetupUser()
|
||||||
|
{
|
||||||
|
var user = new Entities.User(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
"max@mustermann.com",
|
||||||
|
"Max",
|
||||||
|
"Mustermann");
|
||||||
|
|
||||||
|
UserRepository
|
||||||
|
.Setup(x => x.GetByIdAsync(It.Is<Guid>(y => y == user.Id)))
|
||||||
|
.ReturnsAsync(user);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||||
|
using CleanArchitecture.Domain.Errors;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.DeleteUser;
|
||||||
|
|
||||||
|
public sealed class DeleteUserCommandValidationTests :
|
||||||
|
ValidationTestBase<DeleteUserCommand, DeleteUserCommandValidation>
|
||||||
|
{
|
||||||
|
public DeleteUserCommandValidationTests() : base(new DeleteUserCommandValidation())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Valid()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand();
|
||||||
|
|
||||||
|
ShouldBeValid(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Empty_User_Id()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(userId: Guid.Empty);
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserEmptyId,
|
||||||
|
"User id may not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeleteUserCommand CreateTestCommand(Guid? userId = null) =>
|
||||||
|
new (userId ?? Guid.NewGuid());
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||||
|
using CleanArchitecture.Domain.Errors;
|
||||||
|
using CleanArchitecture.Domain.Events.User;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.UpdateUser;
|
||||||
|
|
||||||
|
public sealed class UpdateUserCommandHandlerTests
|
||||||
|
{
|
||||||
|
private readonly UpdateUserCommandTestFixture _fixture = new();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Should_Update_User()
|
||||||
|
{
|
||||||
|
var user = _fixture.SetupUser();
|
||||||
|
|
||||||
|
var command = new UpdateUserCommand(
|
||||||
|
user.Id,
|
||||||
|
"test@email.com",
|
||||||
|
"Test",
|
||||||
|
"Email");
|
||||||
|
|
||||||
|
await _fixture.CommandHandler.Handle(command, default);
|
||||||
|
|
||||||
|
_fixture
|
||||||
|
.VerifyNoDomainNotification()
|
||||||
|
.VerifyCommit()
|
||||||
|
.VerifyRaisedEvent<UserUpdatedEvent>(x => x.UserId == command.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Should_Not_Update_Non_Existing_User()
|
||||||
|
{
|
||||||
|
var user = _fixture.SetupUser();
|
||||||
|
|
||||||
|
var command = new UpdateUserCommand(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
"test@email.com",
|
||||||
|
"Test",
|
||||||
|
"Email");
|
||||||
|
|
||||||
|
await _fixture.CommandHandler.Handle(command, default);
|
||||||
|
|
||||||
|
_fixture
|
||||||
|
.VerifyNoCommit()
|
||||||
|
.VerifyNoRaisedEvent<UserUpdatedEvent>()
|
||||||
|
.VerifyAnyDomainNotification()
|
||||||
|
.VerifyExistingNotification(
|
||||||
|
ErrorCodes.ObjectNotFound,
|
||||||
|
$"There is no User with Id {command.UserId}");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||||
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.UpdateUser;
|
||||||
|
|
||||||
|
public sealed class UpdateUserCommandTestFixture : CommandHandlerFixtureBase
|
||||||
|
{
|
||||||
|
public UpdateUserCommandHandler CommandHandler { get; }
|
||||||
|
private Mock<IUserRepository> UserRepository { get; }
|
||||||
|
|
||||||
|
public UpdateUserCommandTestFixture()
|
||||||
|
{
|
||||||
|
UserRepository = new Mock<IUserRepository>();
|
||||||
|
|
||||||
|
CommandHandler = new(
|
||||||
|
Bus.Object,
|
||||||
|
UnitOfWork.Object,
|
||||||
|
NotificationHandler.Object,
|
||||||
|
UserRepository.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entities.User SetupUser()
|
||||||
|
{
|
||||||
|
var user = new Entities.User(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
"max@mustermann.com",
|
||||||
|
"Max",
|
||||||
|
"Mustermann");
|
||||||
|
|
||||||
|
UserRepository
|
||||||
|
.Setup(x => x.GetByIdAsync(It.Is<Guid>(y => y == user.Id)))
|
||||||
|
.ReturnsAsync(user);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||||
|
using CleanArchitecture.Domain.Errors;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.UpdateUser;
|
||||||
|
|
||||||
|
public sealed class UpdateUserCommandValidationTests :
|
||||||
|
ValidationTestBase<UpdateUserCommand, UpdateUserCommandValidation>
|
||||||
|
{
|
||||||
|
public UpdateUserCommandValidationTests() : base(new UpdateUserCommandValidation())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Valid()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand();
|
||||||
|
|
||||||
|
ShouldBeValid(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Empty_User_Id()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(userId: Guid.Empty);
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserEmptyId,
|
||||||
|
"User id may not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Empty_Email()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(email: string.Empty);
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserInvalidEmail,
|
||||||
|
"Email is not a valid email address");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Invalid_Email()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(email: "not a email");
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserInvalidEmail,
|
||||||
|
"Email is not a valid email address");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Email_Exceeds_Max_Length()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(email: new string('a', 320) + "@test.com");
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserEmailExceedsMaxLength,
|
||||||
|
"Email may not be longer than 320 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Empty_Surname()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(surName: "");
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserEmptySurname,
|
||||||
|
"Surname may not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Surname_Exceeds_Max_Length()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(surName: new string('a', 101));
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserSurnameExceedsMaxLength,
|
||||||
|
"Surname may not be longer than 100 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Empty_Given_Name()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(givenName: "");
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserEmptyGivenName,
|
||||||
|
"Given name may not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Be_Invalid_For_Given_Name_Exceeds_Max_Length()
|
||||||
|
{
|
||||||
|
var command = CreateTestCommand(givenName: new string('a', 101));
|
||||||
|
|
||||||
|
ShouldHaveSingleError(
|
||||||
|
command,
|
||||||
|
DomainErrorCodes.UserGivenNameExceedsMaxLength,
|
||||||
|
"Given name may not be longer than 100 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
private UpdateUserCommand CreateTestCommand(
|
||||||
|
Guid? userId = null,
|
||||||
|
string? email = null,
|
||||||
|
string? surName = null,
|
||||||
|
string? givenName = null) =>
|
||||||
|
new (
|
||||||
|
userId ?? Guid.NewGuid(),
|
||||||
|
email ?? "test@email.com",
|
||||||
|
surName ?? "test",
|
||||||
|
givenName ?? "email");
|
||||||
|
}
|
@ -10,9 +10,4 @@
|
|||||||
<PackageReference Include="MediatR" Version="12.0.1" />
|
<PackageReference Include="MediatR" Version="12.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Commands\Users\CreateUser" />
|
|
||||||
<Folder Include="Commands\Users\UpdateUser" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||||
|
|
||||||
|
public sealed class CreateUserCommand : CommandBase
|
||||||
|
{
|
||||||
|
private static readonly CreateUserCommandValidation _validation = new();
|
||||||
|
|
||||||
|
public Guid UserId { get; }
|
||||||
|
public string Email { get; }
|
||||||
|
public string Surname { get; }
|
||||||
|
public string GivenName { get; }
|
||||||
|
|
||||||
|
public CreateUserCommand(
|
||||||
|
Guid userId,
|
||||||
|
string email,
|
||||||
|
string surname,
|
||||||
|
string givenName) : base(userId)
|
||||||
|
{
|
||||||
|
UserId = userId;
|
||||||
|
Email = email;
|
||||||
|
Surname = surname;
|
||||||
|
GivenName = givenName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsValid()
|
||||||
|
{
|
||||||
|
ValidationResult = _validation.Validate(this);
|
||||||
|
return ValidationResult.IsValid;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CleanArchitecture.Domain.Entities;
|
||||||
|
using CleanArchitecture.Domain.Errors;
|
||||||
|
using CleanArchitecture.Domain.Events.User;
|
||||||
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
|
using CleanArchitecture.Domain.Notifications;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||||
|
|
||||||
|
public sealed class CreateUserCommandHandler : CommandHandlerBase,
|
||||||
|
IRequestHandler<CreateUserCommand>
|
||||||
|
{
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
|
||||||
|
public CreateUserCommandHandler(
|
||||||
|
IMediatorHandler bus,
|
||||||
|
IUnitOfWork unitOfWork,
|
||||||
|
INotificationHandler<DomainNotification> notifications,
|
||||||
|
IUserRepository userRepository) : base(bus, unitOfWork, notifications)
|
||||||
|
{
|
||||||
|
_userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(CreateUserCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!await TestValidityAsync(request))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingUser = await _userRepository.GetByIdAsync(request.UserId);
|
||||||
|
|
||||||
|
if (existingUser != null)
|
||||||
|
{
|
||||||
|
await _bus.RaiseEventAsync(
|
||||||
|
new DomainNotification(
|
||||||
|
request.MessageType,
|
||||||
|
$"There is already a User with Id {request.UserId}",
|
||||||
|
DomainErrorCodes.UserAlreadyExists));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = new User(
|
||||||
|
request.UserId,
|
||||||
|
request.Email,
|
||||||
|
request.Surname,
|
||||||
|
request.GivenName);
|
||||||
|
|
||||||
|
_userRepository.Add(user);
|
||||||
|
|
||||||
|
if (await CommitAsync())
|
||||||
|
{
|
||||||
|
await _bus.RaiseEventAsync(new UserCreatedEvent(user.Id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
using CleanArchitecture.Domain.Errors;
|
||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||||
|
|
||||||
|
public sealed class CreateUserCommandValidation : AbstractValidator<CreateUserCommand>
|
||||||
|
{
|
||||||
|
public CreateUserCommandValidation()
|
||||||
|
{
|
||||||
|
AddRuleForId();
|
||||||
|
AddRuleForEmail();
|
||||||
|
AddRuleForSurname();
|
||||||
|
AddRuleForGivenName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRuleForId()
|
||||||
|
{
|
||||||
|
RuleFor(cmd => cmd.UserId)
|
||||||
|
.NotEmpty()
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserEmptyId)
|
||||||
|
.WithMessage("User id may not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRuleForEmail()
|
||||||
|
{
|
||||||
|
RuleFor(cmd => cmd.Email)
|
||||||
|
.EmailAddress()
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserInvalidEmail)
|
||||||
|
.WithMessage("Email is not a valid email address")
|
||||||
|
.MaximumLength(320)
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength)
|
||||||
|
.WithMessage("Email may not be longer than 320 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRuleForSurname()
|
||||||
|
{
|
||||||
|
RuleFor(cmd => cmd.Surname)
|
||||||
|
.NotEmpty()
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserEmptySurname)
|
||||||
|
.WithMessage("Surname may not be empty")
|
||||||
|
.MaximumLength(100)
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserSurnameExceedsMaxLength)
|
||||||
|
.WithMessage("Surname may not be longer than 100 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRuleForGivenName()
|
||||||
|
{
|
||||||
|
RuleFor(cmd => cmd.GivenName)
|
||||||
|
.NotEmpty()
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserEmptyGivenName)
|
||||||
|
.WithMessage("Given name may not be empty")
|
||||||
|
.MaximumLength(100)
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserGivenNameExceedsMaxLength)
|
||||||
|
.WithMessage("Given name may not be longer than 100 characters");
|
||||||
|
}
|
||||||
|
}
|
@ -45,7 +45,7 @@ public sealed class DeleteUserCommandHandler : CommandHandlerBase,
|
|||||||
|
|
||||||
_userRepository.Remove(user);
|
_userRepository.Remove(user);
|
||||||
|
|
||||||
if (!await CommitAsync())
|
if (await CommitAsync())
|
||||||
{
|
{
|
||||||
await _bus.RaiseEventAsync(new UserDeletedEvent(request.UserId));
|
await _bus.RaiseEventAsync(new UserDeletedEvent(request.UserId));
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||||
|
|
||||||
|
public sealed class UpdateUserCommand : CommandBase
|
||||||
|
{
|
||||||
|
private readonly UpdateUserCommandValidation _validation = new();
|
||||||
|
|
||||||
|
public Guid UserId { get; }
|
||||||
|
public string Email { get; }
|
||||||
|
public string Surname { get; }
|
||||||
|
public string GivenName { get; }
|
||||||
|
|
||||||
|
public UpdateUserCommand(
|
||||||
|
Guid userId,
|
||||||
|
string email,
|
||||||
|
string surname,
|
||||||
|
string givenName) : base(userId)
|
||||||
|
{
|
||||||
|
UserId = userId;
|
||||||
|
Email = email;
|
||||||
|
Surname = surname;
|
||||||
|
GivenName = givenName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsValid()
|
||||||
|
{
|
||||||
|
ValidationResult = _validation.Validate(this);
|
||||||
|
return ValidationResult.IsValid;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CleanArchitecture.Domain.Errors;
|
||||||
|
using CleanArchitecture.Domain.Events.User;
|
||||||
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
|
using CleanArchitecture.Domain.Notifications;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||||
|
|
||||||
|
public sealed class UpdateUserCommandHandler : CommandHandlerBase,
|
||||||
|
IRequestHandler<UpdateUserCommand>
|
||||||
|
{
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
|
||||||
|
public UpdateUserCommandHandler(
|
||||||
|
IMediatorHandler bus,
|
||||||
|
IUnitOfWork unitOfWork,
|
||||||
|
INotificationHandler<DomainNotification> notifications,
|
||||||
|
IUserRepository userRepository) : base(bus, unitOfWork, notifications)
|
||||||
|
{
|
||||||
|
_userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(UpdateUserCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!await TestValidityAsync(request))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = await _userRepository.GetByIdAsync(request.UserId);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
await _bus.RaiseEventAsync(
|
||||||
|
new DomainNotification(
|
||||||
|
request.MessageType,
|
||||||
|
$"There is no User with Id {request.UserId}",
|
||||||
|
ErrorCodes.ObjectNotFound));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.SetEmail(request.Email);
|
||||||
|
user.SetSurname(request.Surname);
|
||||||
|
user.SetGivenName(request.GivenName);
|
||||||
|
|
||||||
|
_userRepository.Update(user);
|
||||||
|
|
||||||
|
if (await CommitAsync())
|
||||||
|
{
|
||||||
|
await _bus.RaiseEventAsync(new UserUpdatedEvent(user.Id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
using CleanArchitecture.Domain.Errors;
|
||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||||
|
|
||||||
|
public sealed class UpdateUserCommandValidation : AbstractValidator<UpdateUserCommand>
|
||||||
|
{
|
||||||
|
public UpdateUserCommandValidation()
|
||||||
|
{
|
||||||
|
AddRuleForId();
|
||||||
|
AddRuleForEmail();
|
||||||
|
AddRuleForSurname();
|
||||||
|
AddRuleForGivenName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRuleForId()
|
||||||
|
{
|
||||||
|
RuleFor(cmd => cmd.UserId)
|
||||||
|
.NotEmpty()
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserEmptyId)
|
||||||
|
.WithMessage("User id may not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRuleForEmail()
|
||||||
|
{
|
||||||
|
RuleFor(cmd => cmd.Email)
|
||||||
|
.EmailAddress()
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserInvalidEmail)
|
||||||
|
.WithMessage("Email is not a valid email address")
|
||||||
|
.MaximumLength(320)
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength)
|
||||||
|
.WithMessage("Email may not be longer than 320 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRuleForSurname()
|
||||||
|
{
|
||||||
|
RuleFor(cmd => cmd.Surname)
|
||||||
|
.NotEmpty()
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserEmptySurname)
|
||||||
|
.WithMessage("Surname may not be empty")
|
||||||
|
.MaximumLength(100)
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserSurnameExceedsMaxLength)
|
||||||
|
.WithMessage("Surname may not be longer than 100 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRuleForGivenName()
|
||||||
|
{
|
||||||
|
RuleFor(cmd => cmd.GivenName)
|
||||||
|
.NotEmpty()
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserEmptyGivenName)
|
||||||
|
.WithMessage("Given name may not be empty")
|
||||||
|
.MaximumLength(100)
|
||||||
|
.WithErrorCode(DomainErrorCodes.UserGivenNameExceedsMaxLength)
|
||||||
|
.WithMessage("Given name may not be longer than 100 characters");
|
||||||
|
}
|
||||||
|
}
|
@ -2,5 +2,15 @@ namespace CleanArchitecture.Domain.Errors;
|
|||||||
|
|
||||||
public static class DomainErrorCodes
|
public static class DomainErrorCodes
|
||||||
{
|
{
|
||||||
|
// User Validation
|
||||||
public const string UserEmptyId = "USER_EMPTY_ID";
|
public const string UserEmptyId = "USER_EMPTY_ID";
|
||||||
|
public const string UserEmptySurname = "USER_EMPTY_SURNAME";
|
||||||
|
public const string UserEmptyGivenName = "USER_EMPTY_GIVEN_NAME";
|
||||||
|
public const string UserEmailExceedsMaxLength = "USER_EMAIL_EXCEEDS_MAX_LENGTH";
|
||||||
|
public const string UserSurnameExceedsMaxLength = "USER_SURNAME_EXCEEDS_MAX_LENGTH";
|
||||||
|
public const string UserGivenNameExceedsMaxLength = "USER_GIVEN_NAME_EXCEEDS_MAX_LENGTH";
|
||||||
|
public const string UserInvalidEmail = "USER_INVALID_EMAIL";
|
||||||
|
|
||||||
|
// User
|
||||||
|
public const string UserAlreadyExists = "USER_ALREADY_EXISTS";
|
||||||
}
|
}
|
@ -6,10 +6,22 @@ using MediatR;
|
|||||||
namespace CleanArchitecture.Domain.EventHandler;
|
namespace CleanArchitecture.Domain.EventHandler;
|
||||||
|
|
||||||
public sealed class UserEventHandler :
|
public sealed class UserEventHandler :
|
||||||
INotificationHandler<UserDeletedEvent>
|
INotificationHandler<UserDeletedEvent>,
|
||||||
|
INotificationHandler<UserCreatedEvent>,
|
||||||
|
INotificationHandler<UserUpdatedEvent>
|
||||||
{
|
{
|
||||||
public Task Handle(UserDeletedEvent notification, CancellationToken cancellationToken)
|
public Task Handle(UserDeletedEvent notification, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task Handle(UserCreatedEvent notification, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Handle(UserUpdatedEvent notification, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
}
|
}
|
13
CleanArchitecture.Domain/Events/User/UserCreatedEvent.cs
Normal file
13
CleanArchitecture.Domain/Events/User/UserCreatedEvent.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Events.User;
|
||||||
|
|
||||||
|
public sealed class UserCreatedEvent : DomainEvent
|
||||||
|
{
|
||||||
|
public Guid UserId { get; }
|
||||||
|
|
||||||
|
public UserCreatedEvent(Guid userId) : base(userId)
|
||||||
|
{
|
||||||
|
UserId = userId;
|
||||||
|
}
|
||||||
|
}
|
13
CleanArchitecture.Domain/Events/User/UserUpdatedEvent.cs
Normal file
13
CleanArchitecture.Domain/Events/User/UserUpdatedEvent.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Events.User;
|
||||||
|
|
||||||
|
public sealed class UserUpdatedEvent : DomainEvent
|
||||||
|
{
|
||||||
|
public Guid UserId { get; }
|
||||||
|
|
||||||
|
public UserUpdatedEvent(Guid userId) : base(userId)
|
||||||
|
{
|
||||||
|
UserId = userId;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
|
using CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||||
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||||
|
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||||
using CleanArchitecture.Domain.EventHandler;
|
using CleanArchitecture.Domain.EventHandler;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
using CleanArchitecture.Domain.Events.User;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
@ -11,6 +13,8 @@ public static class ServiceCollectionExtension
|
|||||||
public static IServiceCollection AddCommandHandlers(this IServiceCollection services)
|
public static IServiceCollection AddCommandHandlers(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
// User
|
// User
|
||||||
|
services.AddScoped<IRequestHandler<CreateUserCommand>, CreateUserCommandHandler>();
|
||||||
|
services.AddScoped<IRequestHandler<UpdateUserCommand>, UpdateUserCommandHandler>();
|
||||||
services.AddScoped<IRequestHandler<DeleteUserCommand>, DeleteUserCommandHandler>();
|
services.AddScoped<IRequestHandler<DeleteUserCommand>, DeleteUserCommandHandler>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
@ -19,6 +23,8 @@ public static class ServiceCollectionExtension
|
|||||||
public static IServiceCollection AddNotificationHandlers(this IServiceCollection services)
|
public static IServiceCollection AddNotificationHandlers(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
// User
|
// User
|
||||||
|
services.AddScoped<INotificationHandler<UserCreatedEvent>, UserEventHandler>();
|
||||||
|
services.AddScoped<INotificationHandler<UserUpdatedEvent>, UserEventHandler>();
|
||||||
services.AddScoped<INotificationHandler<UserDeletedEvent>, UserEventHandler>();
|
services.AddScoped<INotificationHandler<UserDeletedEvent>, UserEventHandler>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
|
Loading…
Reference in New Issue
Block a user