0
0
mirror of https://github.com/alex289/CleanArchitecture.git synced 2025-08-30 15:25:33 +00:00

Add tenant entity

This commit is contained in:
alex289 2023-08-27 19:09:23 +02:00
parent 65d15d383b
commit b0d96d8b4d
No known key found for this signature in database
GPG Key ID: 573F77CD2D87F863
26 changed files with 133 additions and 77 deletions

View File

@ -25,6 +25,7 @@ public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture
{ {
var user = new User( var user = new User(
ExistingUserId, ExistingUserId,
Guid.NewGuid(),
"max@mustermann.com", "max@mustermann.com",
"Max", "Max",
"Mustermann", "Mustermann",
@ -40,6 +41,7 @@ public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture
{ {
var user = new User( var user = new User(
ExistingUserId, ExistingUserId,
Guid.NewGuid(),
"max@mustermann.com", "max@mustermann.com",
"Max", "Max",
"Mustermann", "Mustermann",

View File

@ -26,6 +26,7 @@ public sealed class GetUserByIdTestFixture : QueryHandlerBaseFixture
{ {
var user = new User( var user = new User(
ExistingUserId, ExistingUserId,
Guid.NewGuid(),
"max@mustermann.com", "max@mustermann.com",
"Max", "Max",
"Mustermann", "Mustermann",
@ -41,6 +42,7 @@ public sealed class GetUserByIdTestFixture : QueryHandlerBaseFixture
{ {
var user = new User( var user = new User(
ExistingUserId, ExistingUserId,
Guid.NewGuid(),
"max@mustermann.com", "max@mustermann.com",
"Max", "Max",
"Mustermann", "Mustermann",

View File

@ -46,6 +46,7 @@ public sealed class UserService : IUserService
await _bus.SendCommandAsync(new CreateUserCommand( await _bus.SendCommandAsync(new CreateUserCommand(
userId, userId,
user.TenantId,
user.Email, user.Email,
user.FirstName, user.FirstName,
user.LastName, user.LastName,

View File

@ -27,6 +27,7 @@ public sealed class ChangePasswordCommandTestFixture : CommandHandlerFixtureBase
public Entities.User SetupUser() public Entities.User SetupUser()
{ {
var user = new Entities.User( var user = new Entities.User(
Guid.NewGuid(),
Guid.NewGuid(), Guid.NewGuid(),
"max@mustermann.com", "max@mustermann.com",
"Max", "Max",

View File

@ -16,6 +16,7 @@ public sealed class CreateUserCommandHandlerTests
_fixture.SetupUser(); _fixture.SetupUser();
var command = new CreateUserCommand( var command = new CreateUserCommand(
Guid.NewGuid(),
Guid.NewGuid(), Guid.NewGuid(),
"test@email.com", "test@email.com",
"Test", "Test",
@ -37,6 +38,7 @@ public sealed class CreateUserCommandHandlerTests
var command = new CreateUserCommand( var command = new CreateUserCommand(
user.Id, user.Id,
Guid.NewGuid(),
"test@email.com", "test@email.com",
"Test", "Test",
"Email", "Email",

View File

@ -25,6 +25,7 @@ public sealed class CreateUserCommandTestFixture : CommandHandlerFixtureBase
public Entities.User SetupUser() public Entities.User SetupUser()
{ {
var user = new Entities.User( var user = new Entities.User(
Guid.NewGuid(),
Guid.NewGuid(), Guid.NewGuid(),
"max@mustermann.com", "max@mustermann.com",
"Max", "Max",

View File

@ -178,6 +178,7 @@ public sealed class CreateUserCommandValidationTests :
private static CreateUserCommand CreateTestCommand( private static CreateUserCommand CreateTestCommand(
Guid? userId = null, Guid? userId = null,
Guid? tenantId = null,
string? email = null, string? email = null,
string? firstName = null, string? firstName = null,
string? lastName = null, string? lastName = null,
@ -185,6 +186,7 @@ public sealed class CreateUserCommandValidationTests :
{ {
return new( return new(
userId ?? Guid.NewGuid(), userId ?? Guid.NewGuid(),
tenantId ?? Guid.NewGuid(),
email ?? "test@email.com", email ?? "test@email.com",
firstName ?? "test", firstName ?? "test",
lastName ?? "email", lastName ?? "email",

View File

@ -26,6 +26,7 @@ public sealed class DeleteUserCommandTestFixture : CommandHandlerFixtureBase
public Entities.User SetupUser() public Entities.User SetupUser()
{ {
var user = new Entities.User( var user = new Entities.User(
Guid.NewGuid(),
Guid.NewGuid(), Guid.NewGuid(),
"max@mustermann.com", "max@mustermann.com",
"Max", "Max",

View File

@ -37,6 +37,7 @@ public sealed class LoginUserCommandTestFixture : CommandHandlerFixtureBase
public Entities.User SetupUser() public Entities.User SetupUser()
{ {
var user = new Entities.User( var user = new Entities.User(
Guid.NewGuid(),
Guid.NewGuid(), Guid.NewGuid(),
"max@mustermann.com", "max@mustermann.com",
"Max", "Max",

View File

@ -71,6 +71,7 @@ public sealed class UpdateUserCommandHandlerTests
_fixture.UserRepository _fixture.UserRepository
.GetByEmailAsync(command.Email) .GetByEmailAsync(command.Email)
.Returns(new Entities.User( .Returns(new Entities.User(
Guid.NewGuid(),
Guid.NewGuid(), Guid.NewGuid(),
command.Email, command.Email,
"Some", "Some",

View File

@ -26,6 +26,7 @@ public sealed class UpdateUserCommandTestFixture : CommandHandlerFixtureBase
public Entities.User SetupUser() public Entities.User SetupUser()
{ {
var user = new Entities.User( var user = new Entities.User(
Guid.NewGuid(),
Guid.NewGuid(), Guid.NewGuid(),
"max@mustermann.com", "max@mustermann.com",
"Max", "Max",

View File

@ -8,12 +8,14 @@ public sealed class CreateUserCommand : CommandBase
public CreateUserCommand( public CreateUserCommand(
Guid userId, Guid userId,
Guid tenantId,
string email, string email,
string firstName, string firstName,
string lastName, string lastName,
string password) : base(userId) string password) : base(userId)
{ {
UserId = userId; UserId = userId;
TenantId = tenantId;
Email = email; Email = email;
FirstName = firstName; FirstName = firstName;
LastName = lastName; LastName = lastName;
@ -21,6 +23,7 @@ public sealed class CreateUserCommand : CommandBase
} }
public Guid UserId { get; } public Guid UserId { get; }
public Guid TenantId { get; }
public string Email { get; } public string Email { get; }
public string FirstName { get; } public string FirstName { get; }
public string LastName { get; } public string LastName { get; }

View File

@ -61,6 +61,7 @@ public sealed class CreateUserCommandHandler : CommandHandlerBase,
var user = new User( var user = new User(
request.UserId, request.UserId,
request.TenantId,
request.Email, request.Email,
request.FirstName, request.FirstName,
request.LastName, request.LastName,

View File

@ -1,3 +1,4 @@
using CleanArchitecture.Domain.Constants;
using CleanArchitecture.Domain.Errors; using CleanArchitecture.Domain.Errors;
using CleanArchitecture.Domain.Extensions.Validation; using CleanArchitecture.Domain.Extensions.Validation;
using FluentValidation; using FluentValidation;
@ -29,7 +30,7 @@ public sealed class CreateUserCommandValidation : AbstractValidator<CreateUserCo
.EmailAddress() .EmailAddress()
.WithErrorCode(DomainErrorCodes.UserInvalidEmail) .WithErrorCode(DomainErrorCodes.UserInvalidEmail)
.WithMessage("Email is not a valid email address") .WithMessage("Email is not a valid email address")
.MaximumLength(320) .MaximumLength(MaxLengths.User.Email)
.WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength) .WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength)
.WithMessage("Email may not be longer than 320 characters"); .WithMessage("Email may not be longer than 320 characters");
} }
@ -40,7 +41,7 @@ public sealed class CreateUserCommandValidation : AbstractValidator<CreateUserCo
.NotEmpty() .NotEmpty()
.WithErrorCode(DomainErrorCodes.UserEmptyFirstName) .WithErrorCode(DomainErrorCodes.UserEmptyFirstName)
.WithMessage("FirstName may not be empty") .WithMessage("FirstName may not be empty")
.MaximumLength(100) .MaximumLength(MaxLengths.User.FirstName)
.WithErrorCode(DomainErrorCodes.UserFirstNameExceedsMaxLength) .WithErrorCode(DomainErrorCodes.UserFirstNameExceedsMaxLength)
.WithMessage("FirstName may not be longer than 100 characters"); .WithMessage("FirstName may not be longer than 100 characters");
} }
@ -51,7 +52,7 @@ public sealed class CreateUserCommandValidation : AbstractValidator<CreateUserCo
.NotEmpty() .NotEmpty()
.WithErrorCode(DomainErrorCodes.UserEmptyLastName) .WithErrorCode(DomainErrorCodes.UserEmptyLastName)
.WithMessage("LastName may not be empty") .WithMessage("LastName may not be empty")
.MaximumLength(100) .MaximumLength(MaxLengths.User.LastName)
.WithErrorCode(DomainErrorCodes.UserLastNameExceedsMaxLength) .WithErrorCode(DomainErrorCodes.UserLastNameExceedsMaxLength)
.WithMessage("LastName may not be longer than 100 characters"); .WithMessage("LastName may not be longer than 100 characters");
} }

View File

@ -1,4 +1,5 @@
using CleanArchitecture.Domain.Errors; using CleanArchitecture.Domain.Constants;
using CleanArchitecture.Domain.Errors;
using CleanArchitecture.Domain.Extensions.Validation; using CleanArchitecture.Domain.Extensions.Validation;
using FluentValidation; using FluentValidation;
@ -18,7 +19,7 @@ public sealed class LoginUserCommandValidation : AbstractValidator<LoginUserComm
.EmailAddress() .EmailAddress()
.WithErrorCode(DomainErrorCodes.UserInvalidEmail) .WithErrorCode(DomainErrorCodes.UserInvalidEmail)
.WithMessage("Email is not a valid email address") .WithMessage("Email is not a valid email address")
.MaximumLength(320) .MaximumLength(MaxLengths.User.Email)
.WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength) .WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength)
.WithMessage("Email may not be longer than 320 characters"); .WithMessage("Email may not be longer than 320 characters");
} }

View File

@ -1,3 +1,4 @@
using CleanArchitecture.Domain.Constants;
using CleanArchitecture.Domain.Errors; using CleanArchitecture.Domain.Errors;
using FluentValidation; using FluentValidation;
@ -28,7 +29,7 @@ public sealed class UpdateUserCommandValidation : AbstractValidator<UpdateUserCo
.EmailAddress() .EmailAddress()
.WithErrorCode(DomainErrorCodes.UserInvalidEmail) .WithErrorCode(DomainErrorCodes.UserInvalidEmail)
.WithMessage("Email is not a valid email address") .WithMessage("Email is not a valid email address")
.MaximumLength(320) .MaximumLength(MaxLengths.User.Email)
.WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength) .WithErrorCode(DomainErrorCodes.UserEmailExceedsMaxLength)
.WithMessage("Email may not be longer than 320 characters"); .WithMessage("Email may not be longer than 320 characters");
} }
@ -39,7 +40,7 @@ public sealed class UpdateUserCommandValidation : AbstractValidator<UpdateUserCo
.NotEmpty() .NotEmpty()
.WithErrorCode(DomainErrorCodes.UserEmptyFirstName) .WithErrorCode(DomainErrorCodes.UserEmptyFirstName)
.WithMessage("FirstName may not be empty") .WithMessage("FirstName may not be empty")
.MaximumLength(100) .MaximumLength(MaxLengths.User.FirstName)
.WithErrorCode(DomainErrorCodes.UserFirstNameExceedsMaxLength) .WithErrorCode(DomainErrorCodes.UserFirstNameExceedsMaxLength)
.WithMessage("FirstName may not be longer than 100 characters"); .WithMessage("FirstName may not be longer than 100 characters");
} }
@ -50,7 +51,7 @@ public sealed class UpdateUserCommandValidation : AbstractValidator<UpdateUserCo
.NotEmpty() .NotEmpty()
.WithErrorCode(DomainErrorCodes.UserEmptyLastName) .WithErrorCode(DomainErrorCodes.UserEmptyLastName)
.WithMessage("LastName may not be empty") .WithMessage("LastName may not be empty")
.MaximumLength(100) .MaximumLength(MaxLengths.User.LastName)
.WithErrorCode(DomainErrorCodes.UserLastNameExceedsMaxLength) .WithErrorCode(DomainErrorCodes.UserLastNameExceedsMaxLength)
.WithMessage("LastName may not be longer than 100 characters"); .WithMessage("LastName may not be longer than 100 characters");
} }

View File

@ -0,0 +1,12 @@
using System;
namespace CleanArchitecture.Domain.Constants;
public static class Ids
{
public static class Seed
{
public static readonly Guid UserId = new("7e3892c0-9374-49fa-a3fd-53db637a40ae");
public static readonly Guid TenantId = new("b542bf25-134c-47a2-a0df-84ed14d03c4a");
}
}

View File

@ -0,0 +1,17 @@
namespace CleanArchitecture.Domain.Constants;
public sealed class MaxLengths
{
public static class User
{
public const int Email = 320;
public const int FirstName = 100;
public const int LastName = 100;
public const int Password = 128;
}
public static class Tenant
{
public const int Name = 255;
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
namespace CleanArchitecture.Domain.Entities;
public class Tenant : Entity
{
public string Name { get; private set; }
public ICollection<User> Users { get; private set; } = new HashSet<User>();
public Tenant(
Guid id,
string name) : base(id)
{
Name = name;
}
}

View File

@ -6,21 +6,6 @@ namespace CleanArchitecture.Domain.Entities;
public class User : Entity public class User : Entity
{ {
public User(
Guid id,
string email,
string firstName,
string lastName,
string password,
UserRole role) : base(id)
{
Email = email;
FirstName = firstName;
LastName = lastName;
Password = password;
Role = role;
}
public string Email { get; private set; } public string Email { get; private set; }
public string FirstName { get; private set; } public string FirstName { get; private set; }
public string LastName { get; private set; } public string LastName { get; private set; }
@ -29,71 +14,43 @@ public class User : Entity
public string FullName => $"{FirstName}, {LastName}"; public string FullName => $"{FirstName}, {LastName}";
[MemberNotNull(nameof(Email))] public Guid TenantId { get; private set; }
public Tenant Tenant { get; private set; } = null!;
public User(
Guid id,
Guid tenantId,
string email,
string firstName,
string lastName,
string password,
UserRole role) : base(id)
{
Email = email;
TenantId = tenantId;
FirstName = firstName;
LastName = lastName;
Password = password;
Role = role;
}
public void SetEmail(string email) public void SetEmail(string email)
{ {
if (email == null)
{
throw new ArgumentNullException(nameof(email));
}
if (email.Length > 320)
{
throw new ArgumentException(
"Email may not be longer than 320 characters.");
}
Email = email; Email = email;
} }
[MemberNotNull(nameof(FirstName))]
public void SetFirstName(string firstName) public void SetFirstName(string firstName)
{ {
if (firstName == null)
{
throw new ArgumentNullException(nameof(firstName));
}
if (firstName.Length > 100)
{
throw new ArgumentException(
"First name may not be longer than 100 characters");
}
FirstName = firstName; FirstName = firstName;
} }
[MemberNotNull(nameof(LastName))]
public void SetLastName(string lastName) public void SetLastName(string lastName)
{ {
if (lastName == null)
{
throw new ArgumentNullException(nameof(lastName));
}
if (lastName.Length > 100)
{
throw new ArgumentException(
"Last name may not be longer than 100 characters");
}
LastName = lastName; LastName = lastName;
} }
[MemberNotNull(nameof(Password))]
public void SetPassword(string password) public void SetPassword(string password)
{ {
if (password == null)
{
throw new ArgumentNullException(nameof(password));
}
if (password.Length > 100)
{
throw new ArgumentException(
"Password may not be longer than 100 characters");
}
Password = password; Password = password;
} }

View File

@ -0,0 +1,21 @@
using CleanArchitecture.Domain.Constants;
using CleanArchitecture.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CleanArchitecture.Infrastructure.Configurations;
public sealed class TenantConfiguration : IEntityTypeConfiguration<Tenant>
{
public void Configure(EntityTypeBuilder<Tenant> builder)
{
builder
.Property(user => user.Name)
.IsRequired()
.HasMaxLength(MaxLengths.Tenant.Name);
builder.HasData(new Tenant(
Ids.Seed.TenantId,
"Admin Tenant"));
}
}

View File

@ -1,4 +1,5 @@
using System; using System;
using CleanArchitecture.Domain.Constants;
using CleanArchitecture.Domain.Entities; using CleanArchitecture.Domain.Entities;
using CleanArchitecture.Domain.Enums; using CleanArchitecture.Domain.Enums;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -13,25 +14,26 @@ public sealed class UserConfiguration : IEntityTypeConfiguration<User>
builder builder
.Property(user => user.Email) .Property(user => user.Email)
.IsRequired() .IsRequired()
.HasMaxLength(320); .HasMaxLength(MaxLengths.User.Email);
builder builder
.Property(user => user.FirstName) .Property(user => user.FirstName)
.IsRequired() .IsRequired()
.HasMaxLength(100); .HasMaxLength(MaxLengths.User.FirstName);
builder builder
.Property(user => user.LastName) .Property(user => user.LastName)
.IsRequired() .IsRequired()
.HasMaxLength(100); .HasMaxLength(MaxLengths.User.LastName);
builder builder
.Property(user => user.Password) .Property(user => user.Password)
.IsRequired() .IsRequired()
.HasMaxLength(128); .HasMaxLength(MaxLengths.User.Password);
builder.HasData(new User( builder.HasData(new User(
Guid.NewGuid(), Ids.Seed.UserId,
Ids.Seed.TenantId,
"admin@email.com", "admin@email.com",
"Admin", "Admin",
"User", "User",

View File

@ -11,9 +11,11 @@ public class ApplicationDbContext : DbContext
} }
public DbSet<User> Users { get; set; } = null!; public DbSet<User> Users { get; set; } = null!;
public DbSet<Tenant> Tenants { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
builder.ApplyConfiguration(new UserConfiguration()); builder.ApplyConfiguration(new UserConfiguration());
builder.ApplyConfiguration(new TenantConfiguration());
} }
} }

View File

@ -32,7 +32,8 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
_fixture.CreatedUserEmail, _fixture.CreatedUserEmail,
"Test", "Test",
"Email", "Email",
_fixture.CreatedUserPassword); _fixture.CreatedUserPassword,
Guid.NewGuid());
var response = await _fixture.ServerClient.PostAsJsonAsync("/api/v1/user", user); var response = await _fixture.ServerClient.PostAsJsonAsync("/api/v1/user", user);

View File

@ -33,6 +33,7 @@ public sealed class GetUsersByIdsTestFixture : TestFixtureBase
{ {
return new User( return new User(
CreatedUserId, CreatedUserId,
Guid.NewGuid(),
"user@user.de", "user@user.de",
"User First Name", "User First Name",
"User Last Name", "User Last Name",

View File

@ -16,6 +16,7 @@ public sealed class UserTestsFixture
ExistingUsers = new List<User> ExistingUsers = new List<User>
{ {
new( new(
Guid.NewGuid(),
Guid.NewGuid(), Guid.NewGuid(),
"test@test.de", "test@test.de",
"Test First Name", "Test First Name",
@ -23,6 +24,7 @@ public sealed class UserTestsFixture
"Test Password", "Test Password",
UserRole.User), UserRole.User),
new( new(
Guid.NewGuid(),
Guid.NewGuid(), Guid.NewGuid(),
"email@Email.de", "email@Email.de",
"Email First Name", "Email First Name",
@ -30,6 +32,7 @@ public sealed class UserTestsFixture
"Email Password", "Email Password",
UserRole.Admin), UserRole.Admin),
new( new(
Guid.NewGuid(),
Guid.NewGuid(), Guid.NewGuid(),
"user@user.de", "user@user.de",
"User First Name", "User First Name",