From 6db004066fa3907a05307a001cc8c4b1072d1bff Mon Sep 17 00:00:00 2001 From: Aaron Po Date: Sat, 20 Jun 2026 00:02:27 -0400 Subject: [PATCH] Migrate UserManagement to a vertical slice (Features.UserManagement) Same pattern as the Breweries migration: Service.UserManagement and the UserAccount repository fold into Features.UserManagement, with MediatR queries replacing UserService. UpdateUserCommand/Handler carry forward IUserService.UpdateAsync as-is (it has no HTTP route today, and adding one is a separate decision from this migration). Adds handler/repository test coverage that didn't exist before, since Service.UserManagement had no test project. --- web/backend/API/API.Core/API.Core.csproj | 2 +- web/backend/API/API.Core/Dockerfile | 3 +- web/backend/API/API.Core/Program.cs | 12 +++-- web/backend/Core.slnx | 3 +- .../Commands/UpdateUserHandlerTests.cs | 22 ++++++++ .../Features.UserManagement.Tests.csproj | 26 ++++++++++ .../Queries/GetAllUsersHandlerTests.cs | 23 +++++++++ .../Queries/GetUserByIdHandlerTests.cs | 41 +++++++++++++++ .../Repository/TestConnectionFactory.cs | 11 ++++ .../Repository/UserAccountRepositoryTests.cs} | 7 ++- .../Commands/UpdateUser/UpdateUserCommand.cs | 10 ++++ .../Commands/UpdateUser/UpdateUserHandler.cs | 15 ++++++ .../Controllers/UserController.cs | 14 +++--- ...erManagementServiceCollectionExtensions.cs | 16 ++++++ .../Features.UserManagement.csproj | 20 ++++++++ .../Queries/GetAllUsers/GetAllUsersHandler.cs | 16 ++++++ .../Queries/GetAllUsers/GetAllUsersQuery.cs | 9 ++++ .../Queries/GetUserById/GetUserByIdHandler.cs | 23 +++++++++ .../Queries/GetUserById/GetUserByIdQuery.cs | 9 ++++ .../Repository}/IUserAccountRepository.cs | 2 +- .../Repository}/UserAccountRepository.cs | 4 +- .../Service.UserManagement.csproj | 13 ----- .../User/IUserService.cs | 34 ------------- .../User/UserService.cs | 50 ------------------- 24 files changed, 267 insertions(+), 118 deletions(-) create mode 100644 web/backend/Features/Features.UserManagement.Tests/Commands/UpdateUserHandlerTests.cs create mode 100644 web/backend/Features/Features.UserManagement.Tests/Features.UserManagement.Tests.csproj create mode 100644 web/backend/Features/Features.UserManagement.Tests/Queries/GetAllUsersHandlerTests.cs create mode 100644 web/backend/Features/Features.UserManagement.Tests/Queries/GetUserByIdHandlerTests.cs create mode 100644 web/backend/Features/Features.UserManagement.Tests/Repository/TestConnectionFactory.cs rename web/backend/{Infrastructure/Infrastructure.Repository.Tests/UserAccount/UserAccountRepository.test.cs => Features/Features.UserManagement.Tests/Repository/UserAccountRepositoryTests.cs} (97%) create mode 100644 web/backend/Features/Features.UserManagement/Commands/UpdateUser/UpdateUserCommand.cs create mode 100644 web/backend/Features/Features.UserManagement/Commands/UpdateUser/UpdateUserHandler.cs rename web/backend/{API/API.Core => Features/Features.UserManagement}/Controllers/UserController.cs (74%) create mode 100644 web/backend/Features/Features.UserManagement/DependencyInjection/FeaturesUserManagementServiceCollectionExtensions.cs create mode 100644 web/backend/Features/Features.UserManagement/Features.UserManagement.csproj create mode 100644 web/backend/Features/Features.UserManagement/Queries/GetAllUsers/GetAllUsersHandler.cs create mode 100644 web/backend/Features/Features.UserManagement/Queries/GetAllUsers/GetAllUsersQuery.cs create mode 100644 web/backend/Features/Features.UserManagement/Queries/GetUserById/GetUserByIdHandler.cs create mode 100644 web/backend/Features/Features.UserManagement/Queries/GetUserById/GetUserByIdQuery.cs rename web/backend/{Infrastructure/Infrastructure.Repository/UserAccount => Features/Features.UserManagement/Repository}/IUserAccountRepository.cs (97%) rename web/backend/{Infrastructure/Infrastructure.Repository/UserAccount => Features/Features.UserManagement/Repository}/UserAccountRepository.cs (98%) delete mode 100644 web/backend/Service/Service.UserManagement/Service.UserManagement.csproj delete mode 100644 web/backend/Service/Service.UserManagement/User/IUserService.cs delete mode 100644 web/backend/Service/Service.UserManagement/User/UserService.cs diff --git a/web/backend/API/API.Core/API.Core.csproj b/web/backend/API/API.Core/API.Core.csproj index fa52cc5..b82cf27 100644 --- a/web/backend/API/API.Core/API.Core.csproj +++ b/web/backend/API/API.Core/API.Core.csproj @@ -33,8 +33,8 @@ - + diff --git a/web/backend/API/API.Core/Dockerfile b/web/backend/API/API.Core/Dockerfile index 5926b1d..a657919 100644 --- a/web/backend/API/API.Core/Dockerfile +++ b/web/backend/API/API.Core/Dockerfile @@ -23,6 +23,8 @@ COPY ["Domain/Domain.Entities/Domain.Entities.csproj", "Domain/Domain.Entities/" COPY ["Domain/Domain.Exceptions/Domain.Exceptions.csproj", "Domain/Domain.Exceptions/"] COPY ["Features/Features.Breweries/Features.Breweries.csproj", "Features/Features.Breweries/"] COPY ["Features/Features.Breweries.Tests/Features.Breweries.Tests.csproj", "Features/Features.Breweries.Tests/"] +COPY ["Features/Features.UserManagement/Features.UserManagement.csproj", "Features/Features.UserManagement/"] +COPY ["Features/Features.UserManagement.Tests/Features.UserManagement.Tests.csproj", "Features/Features.UserManagement.Tests/"] COPY ["Infrastructure/Infrastructure.Email/Infrastructure.Email.csproj", "Infrastructure/Infrastructure.Email/"] COPY ["Infrastructure/Infrastructure.Email.Templates/Infrastructure.Email.Templates.csproj", "Infrastructure/Infrastructure.Email.Templates/"] COPY ["Infrastructure/Infrastructure.Jwt/Infrastructure.Jwt.csproj", "Infrastructure/Infrastructure.Jwt/"] @@ -33,7 +35,6 @@ COPY ["Infrastructure/Infrastructure.Sql/Infrastructure.Sql.csproj", "Infrastruc COPY ["Service/Service.Auth/Service.Auth.csproj", "Service/Service.Auth/"] COPY ["Service/Service.Auth.Tests/Service.Auth.Tests.csproj", "Service/Service.Auth.Tests/"] COPY ["Service/Service.Emails/Service.Emails.csproj", "Service/Service.Emails/"] -COPY ["Service/Service.UserManagement/Service.UserManagement.csproj", "Service/Service.UserManagement/"] COPY ["Shared/Shared.Application/Shared.Application.csproj", "Shared/Shared.Application/"] COPY ["Shared/Shared.Contracts/Shared.Contracts.csproj", "Shared/Shared.Contracts/"] RUN dotnet restore "Core.slnx" diff --git a/web/backend/API/API.Core/Program.cs b/web/backend/API/API.Core/Program.cs index 811a9f3..14cc423 100644 --- a/web/backend/API/API.Core/Program.cs +++ b/web/backend/API/API.Core/Program.cs @@ -2,6 +2,8 @@ using API.Core; using API.Core.Authentication; using Features.Breweries.Controllers; using Features.Breweries.DependencyInjection; +using Features.UserManagement.Controllers; +using Features.UserManagement.DependencyInjection; using FluentValidation; using FluentValidation.AspNetCore; using Infrastructure.Email; @@ -9,11 +11,9 @@ using Infrastructure.Email.Templates.Rendering; using Infrastructure.Jwt; using Infrastructure.PasswordHashing; using Infrastructure.Repository.Auth; -using Infrastructure.Repository.UserAccount; using Infrastructure.Sql; using Service.Auth; using Service.Emails; -using Service.UserManagement.User; using Shared.Application.Behaviors; var builder = WebApplication.CreateBuilder(args); @@ -23,7 +23,8 @@ builder.Services.AddControllers(options => { options.Filters.Add(); }) - .AddApplicationPart(typeof(BreweryController).Assembly); + .AddApplicationPart(typeof(BreweryController).Assembly) + .AddApplicationPart(typeof(UserController).Assembly); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); @@ -32,6 +33,7 @@ builder.Services.AddOpenApi(); // Add FluentValidation builder.Services.AddValidatorsFromAssemblyContaining(); builder.Services.AddValidatorsFromAssembly(typeof(BreweryController).Assembly); +builder.Services.AddValidatorsFromAssembly(typeof(UserController).Assembly); builder.Services.AddFluentValidationAutoValidation(); // Add MediatR. Each Features.* slice's assembly is registered here as it's introduced; @@ -40,6 +42,7 @@ builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblyContaining(); cfg.RegisterServicesFromAssembly(typeof(BreweryController).Assembly); + cfg.RegisterServicesFromAssembly(typeof(UserController).Assembly); cfg.AddOpenBehavior(typeof(ValidationBehavior<,>)); }); @@ -61,11 +64,10 @@ builder.Services.AddSingleton< DefaultSqlConnectionFactory >(); -builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddFeaturesBreweries(); +builder.Services.AddFeaturesUserManagement(); -builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/web/backend/Core.slnx b/web/backend/Core.slnx index 7318055..384490e 100644 --- a/web/backend/Core.slnx +++ b/web/backend/Core.slnx @@ -26,11 +26,12 @@ + + - diff --git a/web/backend/Features/Features.UserManagement.Tests/Commands/UpdateUserHandlerTests.cs b/web/backend/Features/Features.UserManagement.Tests/Commands/UpdateUserHandlerTests.cs new file mode 100644 index 0000000..f972896 --- /dev/null +++ b/web/backend/Features/Features.UserManagement.Tests/Commands/UpdateUserHandlerTests.cs @@ -0,0 +1,22 @@ +using Domain.Entities; +using Features.UserManagement.Commands.UpdateUser; +using Features.UserManagement.Repository; +using Moq; + +namespace Features.UserManagement.Tests.Commands; + +public class UpdateUserHandlerTests +{ + [Fact] + public async Task Handle_DelegatesToRepository() + { + var repoMock = new Mock(); + var handler = new UpdateUserHandler(repoMock.Object); + var user = new UserAccount { UserAccountId = Guid.NewGuid() }; + repoMock.Setup(r => r.UpdateAsync(user)).Returns(Task.CompletedTask); + + await handler.Handle(new UpdateUserCommand(user), CancellationToken.None); + + repoMock.Verify(r => r.UpdateAsync(user), Times.Once); + } +} diff --git a/web/backend/Features/Features.UserManagement.Tests/Features.UserManagement.Tests.csproj b/web/backend/Features/Features.UserManagement.Tests/Features.UserManagement.Tests.csproj new file mode 100644 index 0000000..621ef73 --- /dev/null +++ b/web/backend/Features/Features.UserManagement.Tests/Features.UserManagement.Tests.csproj @@ -0,0 +1,26 @@ + + + net10.0 + enable + enable + false + Features.UserManagement.Tests + + + + + + + + + + + + + + + + + + + diff --git a/web/backend/Features/Features.UserManagement.Tests/Queries/GetAllUsersHandlerTests.cs b/web/backend/Features/Features.UserManagement.Tests/Queries/GetAllUsersHandlerTests.cs new file mode 100644 index 0000000..d5d3014 --- /dev/null +++ b/web/backend/Features/Features.UserManagement.Tests/Queries/GetAllUsersHandlerTests.cs @@ -0,0 +1,23 @@ +using Domain.Entities; +using FluentAssertions; +using Features.UserManagement.Queries.GetAllUsers; +using Features.UserManagement.Repository; +using Moq; + +namespace Features.UserManagement.Tests.Queries; + +public class GetAllUsersHandlerTests +{ + [Fact] + public async Task Handle_PassesLimitAndOffset_ToRepository() + { + var repoMock = new Mock(); + var handler = new GetAllUsersHandler(repoMock.Object); + repoMock.Setup(r => r.GetAllAsync(10, 5)).ReturnsAsync(Array.Empty()); + + var result = await handler.Handle(new GetAllUsersQuery(10, 5), CancellationToken.None); + + result.Should().BeEmpty(); + repoMock.Verify(r => r.GetAllAsync(10, 5), Times.Once); + } +} diff --git a/web/backend/Features/Features.UserManagement.Tests/Queries/GetUserByIdHandlerTests.cs b/web/backend/Features/Features.UserManagement.Tests/Queries/GetUserByIdHandlerTests.cs new file mode 100644 index 0000000..2136c88 --- /dev/null +++ b/web/backend/Features/Features.UserManagement.Tests/Queries/GetUserByIdHandlerTests.cs @@ -0,0 +1,41 @@ +using Domain.Entities; +using Domain.Exceptions; +using FluentAssertions; +using Features.UserManagement.Queries.GetUserById; +using Features.UserManagement.Repository; +using Moq; + +namespace Features.UserManagement.Tests.Queries; + +public class GetUserByIdHandlerTests +{ + private readonly Mock _repoMock = new(); + private readonly GetUserByIdHandler _handler; + + public GetUserByIdHandlerTests() + { + _handler = new GetUserByIdHandler(_repoMock.Object); + } + + [Fact] + public async Task Handle_ReturnsUser_WhenFound() + { + var user = new UserAccount { UserAccountId = Guid.NewGuid(), Username = "test" }; + _repoMock.Setup(r => r.GetByIdAsync(user.UserAccountId)).ReturnsAsync(user); + + var result = await _handler.Handle(new GetUserByIdQuery(user.UserAccountId), CancellationToken.None); + + result.Should().Be(user); + } + + [Fact] + public async Task Handle_Throws_WhenNotFound() + { + var id = Guid.NewGuid(); + _repoMock.Setup(r => r.GetByIdAsync(id)).ReturnsAsync((UserAccount?)null); + + var act = async () => await _handler.Handle(new GetUserByIdQuery(id), CancellationToken.None); + + await act.Should().ThrowAsync(); + } +} diff --git a/web/backend/Features/Features.UserManagement.Tests/Repository/TestConnectionFactory.cs b/web/backend/Features/Features.UserManagement.Tests/Repository/TestConnectionFactory.cs new file mode 100644 index 0000000..1266eb3 --- /dev/null +++ b/web/backend/Features/Features.UserManagement.Tests/Repository/TestConnectionFactory.cs @@ -0,0 +1,11 @@ +using System.Data.Common; +using Infrastructure.Sql; + +namespace Features.UserManagement.Tests.Repository; + +internal class TestConnectionFactory(DbConnection conn) : ISqlConnectionFactory +{ + private readonly DbConnection _conn = conn; + + public DbConnection CreateConnection() => _conn; +} diff --git a/web/backend/Infrastructure/Infrastructure.Repository.Tests/UserAccount/UserAccountRepository.test.cs b/web/backend/Features/Features.UserManagement.Tests/Repository/UserAccountRepositoryTests.cs similarity index 97% rename from web/backend/Infrastructure/Infrastructure.Repository.Tests/UserAccount/UserAccountRepository.test.cs rename to web/backend/Features/Features.UserManagement.Tests/Repository/UserAccountRepositoryTests.cs index 9a60162..ba393ba 100644 --- a/web/backend/Infrastructure/Infrastructure.Repository.Tests/UserAccount/UserAccountRepository.test.cs +++ b/web/backend/Features/Features.UserManagement.Tests/Repository/UserAccountRepositoryTests.cs @@ -1,11 +1,10 @@ using Apps72.Dev.Data.DbMocker; using FluentAssertions; -using Infrastructure.Repository.Tests.Database; -using Infrastructure.Repository.UserAccount; +using Features.UserManagement.Repository; -namespace Infrastructure.Repository.Tests.UserAccount; +namespace Features.UserManagement.Tests.Repository; -public class UserAccountRepositoryTest +public class UserAccountRepositoryTests { private static UserAccountRepository CreateRepo(MockDbConnection conn) => new(new TestConnectionFactory(conn)); diff --git a/web/backend/Features/Features.UserManagement/Commands/UpdateUser/UpdateUserCommand.cs b/web/backend/Features/Features.UserManagement/Commands/UpdateUser/UpdateUserCommand.cs new file mode 100644 index 0000000..05b6775 --- /dev/null +++ b/web/backend/Features/Features.UserManagement/Commands/UpdateUser/UpdateUserCommand.cs @@ -0,0 +1,10 @@ +using Domain.Entities; +using MediatR; + +namespace Features.UserManagement.Commands.UpdateUser; + +/// +/// Updates an existing user account. Not currently exposed via any HTTP route, carried forward +/// from IUserService.UpdateAsync as-is. +/// +public record UpdateUserCommand(UserAccount UserAccount) : IRequest; diff --git a/web/backend/Features/Features.UserManagement/Commands/UpdateUser/UpdateUserHandler.cs b/web/backend/Features/Features.UserManagement/Commands/UpdateUser/UpdateUserHandler.cs new file mode 100644 index 0000000..e1666bd --- /dev/null +++ b/web/backend/Features/Features.UserManagement/Commands/UpdateUser/UpdateUserHandler.cs @@ -0,0 +1,15 @@ +using Features.UserManagement.Repository; +using MediatR; + +namespace Features.UserManagement.Commands.UpdateUser; + +/// +/// Handles by persisting changes to an existing user account. +/// +/// Repository used to persist the updated user account. +public class UpdateUserHandler(IUserAccountRepository repository) + : IRequestHandler +{ + public Task Handle(UpdateUserCommand request, CancellationToken cancellationToken) => + repository.UpdateAsync(request.UserAccount); +} diff --git a/web/backend/API/API.Core/Controllers/UserController.cs b/web/backend/Features/Features.UserManagement/Controllers/UserController.cs similarity index 74% rename from web/backend/API/API.Core/Controllers/UserController.cs rename to web/backend/Features/Features.UserManagement/Controllers/UserController.cs index 972847a..cd499ce 100644 --- a/web/backend/API/API.Core/Controllers/UserController.cs +++ b/web/backend/Features/Features.UserManagement/Controllers/UserController.cs @@ -1,16 +1,18 @@ using Domain.Entities; +using Features.UserManagement.Queries.GetAllUsers; +using Features.UserManagement.Queries.GetUserById; +using MediatR; using Microsoft.AspNetCore.Mvc; -using Service.UserManagement.User; -namespace API.Core.Controllers +namespace Features.UserManagement.Controllers { /// /// Provides read-only endpoints for retrieving user accounts. /// - /// Service used to query user account data. + /// Used to dispatch user queries to their handlers. [ApiController] [Route("api/[controller]")] - public class UserController(IUserService userService) : ControllerBase + public class UserController(IMediator mediator) : ControllerBase { /// /// Retrieves a paginated list of user accounts. @@ -24,7 +26,7 @@ namespace API.Core.Controllers [FromQuery] int? offset ) { - var users = await userService.GetAllAsync(limit, offset); + var users = await mediator.Send(new GetAllUsersQuery(limit, offset)); return Ok(users); } @@ -36,7 +38,7 @@ namespace API.Core.Controllers [HttpGet("{id:guid}")] public async Task> GetById(Guid id) { - var user = await userService.GetByIdAsync(id); + var user = await mediator.Send(new GetUserByIdQuery(id)); return Ok(user); } } diff --git a/web/backend/Features/Features.UserManagement/DependencyInjection/FeaturesUserManagementServiceCollectionExtensions.cs b/web/backend/Features/Features.UserManagement/DependencyInjection/FeaturesUserManagementServiceCollectionExtensions.cs new file mode 100644 index 0000000..2d09f36 --- /dev/null +++ b/web/backend/Features/Features.UserManagement/DependencyInjection/FeaturesUserManagementServiceCollectionExtensions.cs @@ -0,0 +1,16 @@ +using Features.UserManagement.Repository; +using Microsoft.Extensions.DependencyInjection; + +namespace Features.UserManagement.DependencyInjection; + +/// +/// Registers the services owned by the UserManagement feature slice. +/// +public static class FeaturesUserManagementServiceCollectionExtensions +{ + public static IServiceCollection AddFeaturesUserManagement(this IServiceCollection services) + { + services.AddScoped(); + return services; + } +} diff --git a/web/backend/Features/Features.UserManagement/Features.UserManagement.csproj b/web/backend/Features/Features.UserManagement/Features.UserManagement.csproj new file mode 100644 index 0000000..aa9eb8b --- /dev/null +++ b/web/backend/Features/Features.UserManagement/Features.UserManagement.csproj @@ -0,0 +1,20 @@ + + + net10.0 + enable + enable + Features.UserManagement + + + + + + + + + + + + + + diff --git a/web/backend/Features/Features.UserManagement/Queries/GetAllUsers/GetAllUsersHandler.cs b/web/backend/Features/Features.UserManagement/Queries/GetAllUsers/GetAllUsersHandler.cs new file mode 100644 index 0000000..682fd39 --- /dev/null +++ b/web/backend/Features/Features.UserManagement/Queries/GetAllUsers/GetAllUsersHandler.cs @@ -0,0 +1,16 @@ +using Domain.Entities; +using Features.UserManagement.Repository; +using MediatR; + +namespace Features.UserManagement.Queries.GetAllUsers; + +/// +/// Handles by retrieving a paginated list of user accounts. +/// +/// Repository used to query user account data. +public class GetAllUsersHandler(IUserAccountRepository repository) + : IRequestHandler> +{ + public Task> Handle(GetAllUsersQuery request, CancellationToken cancellationToken) => + repository.GetAllAsync(request.Limit, request.Offset); +} diff --git a/web/backend/Features/Features.UserManagement/Queries/GetAllUsers/GetAllUsersQuery.cs b/web/backend/Features/Features.UserManagement/Queries/GetAllUsers/GetAllUsersQuery.cs new file mode 100644 index 0000000..dab6b3b --- /dev/null +++ b/web/backend/Features/Features.UserManagement/Queries/GetAllUsers/GetAllUsersQuery.cs @@ -0,0 +1,9 @@ +using Domain.Entities; +using MediatR; + +namespace Features.UserManagement.Queries.GetAllUsers; + +/// +/// Retrieves a paginated list of user accounts. +/// +public record GetAllUsersQuery(int? Limit, int? Offset) : IRequest>; diff --git a/web/backend/Features/Features.UserManagement/Queries/GetUserById/GetUserByIdHandler.cs b/web/backend/Features/Features.UserManagement/Queries/GetUserById/GetUserByIdHandler.cs new file mode 100644 index 0000000..1fe54a3 --- /dev/null +++ b/web/backend/Features/Features.UserManagement/Queries/GetUserById/GetUserByIdHandler.cs @@ -0,0 +1,23 @@ +using Domain.Entities; +using Domain.Exceptions; +using Features.UserManagement.Repository; +using MediatR; + +namespace Features.UserManagement.Queries.GetUserById; + +/// +/// Handles by looking up the matching user account. +/// +/// Repository used to query user account data. +public class GetUserByIdHandler(IUserAccountRepository repository) + : IRequestHandler +{ + /// Thrown when no user account exists with the given ID. + public async Task Handle(GetUserByIdQuery request, CancellationToken cancellationToken) + { + var user = await repository.GetByIdAsync(request.UserAccountId); + if (user is null) + throw new NotFoundException($"User with ID {request.UserAccountId} not found"); + return user; + } +} diff --git a/web/backend/Features/Features.UserManagement/Queries/GetUserById/GetUserByIdQuery.cs b/web/backend/Features/Features.UserManagement/Queries/GetUserById/GetUserByIdQuery.cs new file mode 100644 index 0000000..b047bc4 --- /dev/null +++ b/web/backend/Features/Features.UserManagement/Queries/GetUserById/GetUserByIdQuery.cs @@ -0,0 +1,9 @@ +using Domain.Entities; +using MediatR; + +namespace Features.UserManagement.Queries.GetUserById; + +/// +/// Retrieves a single user account by its unique identifier. +/// +public record GetUserByIdQuery(Guid UserAccountId) : IRequest; diff --git a/web/backend/Infrastructure/Infrastructure.Repository/UserAccount/IUserAccountRepository.cs b/web/backend/Features/Features.UserManagement/Repository/IUserAccountRepository.cs similarity index 97% rename from web/backend/Infrastructure/Infrastructure.Repository/UserAccount/IUserAccountRepository.cs rename to web/backend/Features/Features.UserManagement/Repository/IUserAccountRepository.cs index 4a57ad7..6854cce 100644 --- a/web/backend/Infrastructure/Infrastructure.Repository/UserAccount/IUserAccountRepository.cs +++ b/web/backend/Features/Features.UserManagement/Repository/IUserAccountRepository.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Repository.UserAccount; +namespace Features.UserManagement.Repository; /// /// Repository for CRUD operations on user account records. diff --git a/web/backend/Infrastructure/Infrastructure.Repository/UserAccount/UserAccountRepository.cs b/web/backend/Features/Features.UserManagement/Repository/UserAccountRepository.cs similarity index 98% rename from web/backend/Infrastructure/Infrastructure.Repository/UserAccount/UserAccountRepository.cs rename to web/backend/Features/Features.UserManagement/Repository/UserAccountRepository.cs index 943042d..41ec7f1 100644 --- a/web/backend/Infrastructure/Infrastructure.Repository/UserAccount/UserAccountRepository.cs +++ b/web/backend/Features/Features.UserManagement/Repository/UserAccountRepository.cs @@ -2,7 +2,7 @@ using System.Data; using System.Data.Common; using Infrastructure.Sql; -namespace Infrastructure.Repository.UserAccount; +namespace Features.UserManagement.Repository; /// /// ADO.NET-based implementation of backed by SQL Server @@ -10,7 +10,7 @@ namespace Infrastructure.Repository.UserAccount; /// /// The factory used to create database connections. public class UserAccountRepository(ISqlConnectionFactory connectionFactory) - : Repository(connectionFactory), + : Infrastructure.Sql.Repository(connectionFactory), IUserAccountRepository { /// diff --git a/web/backend/Service/Service.UserManagement/Service.UserManagement.csproj b/web/backend/Service/Service.UserManagement/Service.UserManagement.csproj deleted file mode 100644 index 5e93f9d..0000000 --- a/web/backend/Service/Service.UserManagement/Service.UserManagement.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - net10.0 - enable - enable - - - - - - - diff --git a/web/backend/Service/Service.UserManagement/User/IUserService.cs b/web/backend/Service/Service.UserManagement/User/IUserService.cs deleted file mode 100644 index bd017eb..0000000 --- a/web/backend/Service/Service.UserManagement/User/IUserService.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Domain.Entities; - -namespace Service.UserManagement.User; - -/// -/// Defines operations for retrieving and updating user accounts. -/// -public interface IUserService -{ - /// - /// Retrieves all user accounts, optionally paginated. - /// - /// The maximum number of results to return, or null for no limit. - /// The number of results to skip, or null to start from the beginning. - /// A collection of entities. - Task> GetAllAsync( - int? limit = null, - int? offset = null - ); - - /// - /// Retrieves a user account by its unique identifier. - /// - /// The unique identifier of the user account. - /// The matching . - Task GetByIdAsync(Guid id); - - /// - /// Updates an existing user account. - /// - /// The user account containing the updated data. - /// A task that completes once the update has finished. - Task UpdateAsync(UserAccount userAccount); -} diff --git a/web/backend/Service/Service.UserManagement/User/UserService.cs b/web/backend/Service/Service.UserManagement/User/UserService.cs deleted file mode 100644 index bfba75a..0000000 --- a/web/backend/Service/Service.UserManagement/User/UserService.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Domain.Entities; -using Domain.Exceptions; -using Infrastructure.Repository.UserAccount; - -namespace Service.UserManagement.User; - -/// -/// Handles retrieval and update of user accounts. -/// -/// Repository used to persist and query user account data. -public class UserService(IUserAccountRepository repository) : IUserService -{ - /// - /// Retrieves all user accounts, optionally paginated. - /// - /// The maximum number of results to return, or null for no limit. - /// The number of results to skip, or null to start from the beginning. - /// A collection of entities. - public async Task> GetAllAsync( - int? limit = null, - int? offset = null - ) - { - return await repository.GetAllAsync(limit, offset); - } - - /// - /// Retrieves a user account by its unique identifier. - /// - /// The unique identifier of the user account. - /// The matching . - /// Thrown when no user account exists with the given . - public async Task GetByIdAsync(Guid id) - { - var user = await repository.GetByIdAsync(id); - if (user is null) - throw new NotFoundException($"User with ID {id} not found"); - return user; - } - - /// - /// Updates an existing user account. - /// - /// The user account containing the updated data. - /// A task that completes once the update has finished. - public async Task UpdateAsync(UserAccount userAccount) - { - await repository.UpdateAsync(userAccount); - } -}