Перепиливание под чистый DDD

This commit is contained in:
Халимов Рустам
2026-03-22 23:59:33 +03:00
parent 5da1a2f45d
commit 6e532b021d
302 changed files with 3595 additions and 3679 deletions
@@ -0,0 +1,21 @@
using System;
namespace Knot.Modules.Auth.Application.Users.Auth;
public record AuthResponseDto(
string Token,
AuthUserDto User
);
public record AuthUserDto(
Guid Id,
string Username,
string DisplayName,
string? Email,
string? Bio,
string? Avatar,
DateTime? Birthday,
bool IsOnline,
DateTime CreatedAt
);
@@ -0,0 +1,45 @@
using Knot.Shared.Kernel;
using Knot.Modules.Auth.Domain;
using Knot.Modules.Auth.Application.Users.Auth;
namespace Knot.Modules.Auth.Application.Users.GetMe;
public sealed record GetMeQuery(Guid UserId) : IQuery<AuthResponseDto>;
internal sealed class GetMeQueryHandler : IQueryHandler<GetMeQuery, AuthResponseDto>
{
private readonly IUserRepository _userRepository;
public GetMeQueryHandler(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public async Task<Result<AuthResponseDto>> Handle(GetMeQuery request, CancellationToken cancellationToken)
{
var user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);
if (user == null)
{
return Result.Failure<AuthResponseDto>(AuthErrors.UserNotFound);
}
var response = new AuthResponseDto(
string.Empty,
new AuthUserDto(
user.Id,
user.Username,
user.DisplayName,
user.Email,
user.Bio,
user.Avatar,
user.Birthday,
true,
user.CreatedAt
)
);
return Result.Success(response);
}
}
@@ -0,0 +1,53 @@
using Knot.Modules.Auth.Domain;
using Knot.Modules.Auth.Application.Abstractions;
using Knot.Shared.Kernel;
using Knot.Modules.Auth.Application.Users.Auth;
using BCrypt.Net;
namespace Knot.Modules.Auth.Application.Users.Login;
/// <summary>
/// Команда для входа пользователя. Возвращает AuthResponseDto.
/// </summary>
public sealed record LoginUserCommand(string Username, string Password) : ICommand<AuthResponseDto>;
public sealed class LoginUserCommandHandler : ICommandHandler<LoginUserCommand, AuthResponseDto>
{
private readonly IUserRepository _userRepository;
private readonly IJwtTokenProvider _tokenProvider;
public LoginUserCommandHandler(IUserRepository userRepository, IJwtTokenProvider tokenProvider)
{
_userRepository = userRepository;
_tokenProvider = tokenProvider;
}
public async Task<Result<AuthResponseDto>> Handle(LoginUserCommand request, CancellationToken cancellationToken)
{
var user = await _userRepository.GetByUsernameAsync(request.Username, cancellationToken);
if (user is null || !BCrypt.Net.BCrypt.Verify(request.Password, user.PasswordHash))
{
return Result.Failure<AuthResponseDto>(AuthErrors.IdentityInvalidCredentials);
}
string token = _tokenProvider.Generate(user);
return Result.Success(new AuthResponseDto(
token,
new AuthUserDto(
user.Id,
user.Username,
user.DisplayName,
user.Email,
user.Bio,
user.Avatar,
user.Birthday,
true, // IsOnline (placeholder)
user.CreatedAt
)
));
}
}
@@ -0,0 +1,89 @@
using Knot.Modules.Auth.Domain;
using Knot.Modules.Auth.Application.Abstractions;
using Knot.Shared.Kernel;
using Knot.Shared.Kernel.Configuration;
using Knot.Modules.Auth.Application.Users.Auth;
using BCrypt.Net;
namespace Knot.Modules.Auth.Application.Users.Register;
/// <summary>
/// Команда для регистрации нового пользователя.
/// </summary>
public sealed record RegisterUserCommand(
string Username,
string Password,
string DisplayName,
string? Email,
string? Bio) : ICommand<AuthResponseDto>;
/// <summary>
/// Обработчик команды регистрации.
/// </summary>
public sealed class RegisterUserCommandHandler : ICommandHandler<RegisterUserCommand, AuthResponseDto>
{
private readonly IUserRepository _userRepository;
private readonly IAuthUnitOfWork _unitOfWork;
private readonly ISettingsService _settings;
private readonly IJwtTokenProvider _tokenProvider;
public RegisterUserCommandHandler(
IUserRepository userRepository,
IAuthUnitOfWork unitOfWork,
ISettingsService settings,
IJwtTokenProvider tokenProvider)
{
_userRepository = userRepository;
_unitOfWork = unitOfWork;
_settings = settings;
_tokenProvider = tokenProvider;
}
public async Task<Result<AuthResponseDto>> Handle(RegisterUserCommand request, CancellationToken cancellationToken)
{
if (!_settings.Current.EnableRegistration)
{
return Result.Failure<AuthResponseDto>(AuthErrors.IdentityRegistrationDisabled);
}
// 1. Проверка уникальности username
if (!await _userRepository.IsUsernameUniqueAsync(request.Username, cancellationToken))
{
return Result.Failure<AuthResponseDto>(AuthErrors.IdentityUsernameNotUnique);
}
// 2. Хеширование пароля (здесь будет вызов сервиса, пока заглушка)
string passwordHash = BCrypt.Net.BCrypt.HashPassword(request.Password);
// 3. Создание сущности
var user = User.Create(
request.Username,
passwordHash,
request.DisplayName,
request.Email,
request.Bio);
// 4. Сохранение
_userRepository.Add(user);
await _unitOfWork.SaveChangesAsync(cancellationToken);
string token = _tokenProvider.Generate(user);
return Result.Success(new AuthResponseDto(
token,
new AuthUserDto(
user.Id,
user.Username,
user.DisplayName,
user.Email,
user.Bio,
user.Avatar,
user.Birthday,
true, // IsOnline (placeholder)
user.CreatedAt
)
));
}
}