mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-07-16 09:37:23 +00:00
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.
This commit is contained in:
@@ -33,8 +33,8 @@
|
||||
<ProjectReference Include="..\..\Infrastructure\Infrastructure.Sql\Infrastructure.Sql.csproj" />
|
||||
<ProjectReference Include="..\..\Infrastructure\Infrastructure.Jwt\Infrastructure.Jwt.csproj" />
|
||||
<ProjectReference Include="..\..\Service\Service.Auth\Service.Auth.csproj" />
|
||||
<ProjectReference Include="..\..\Service\Service.UserManagement\Service.UserManagement.csproj" />
|
||||
<ProjectReference Include="..\..\Features\Features.Breweries\Features.Breweries.csproj" />
|
||||
<ProjectReference Include="..\..\Features\Features.UserManagement\Features.UserManagement.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\Shared.Contracts\Shared.Contracts.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\Shared.Application\Shared.Application.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<GlobalExceptionFilter>();
|
||||
})
|
||||
.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<Program>();
|
||||
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<Program>();
|
||||
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<IUserAccountRepository, UserAccountRepository>();
|
||||
builder.Services.AddScoped<IAuthRepository, AuthRepository>();
|
||||
builder.Services.AddFeaturesBreweries();
|
||||
builder.Services.AddFeaturesUserManagement();
|
||||
|
||||
builder.Services.AddScoped<IUserService, UserService>();
|
||||
builder.Services.AddScoped<ILoginService, LoginService>();
|
||||
builder.Services.AddScoped<IRegisterService, RegisterService>();
|
||||
builder.Services.AddScoped<ITokenService, TokenService>();
|
||||
|
||||
@@ -26,11 +26,12 @@
|
||||
<Folder Name="/Features/">
|
||||
<Project Path="Features/Features.Breweries/Features.Breweries.csproj" />
|
||||
<Project Path="Features/Features.Breweries.Tests/Features.Breweries.Tests.csproj" />
|
||||
<Project Path="Features/Features.UserManagement/Features.UserManagement.csproj" />
|
||||
<Project Path="Features/Features.UserManagement.Tests/Features.UserManagement.Tests.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Service/">
|
||||
<Project Path="Service/Service.Auth.Tests/Service.Auth.Tests.csproj" />
|
||||
<Project Path="Service/Service.Emails/Service.Emails.csproj" />
|
||||
<Project Path="Service/Service.UserManagement/Service.UserManagement.csproj" />
|
||||
<Project Path="Service/Service.Auth/Service.Auth.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Shared/">
|
||||
|
||||
@@ -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<IUserAccountRepository>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<RootNamespace>Features.UserManagement.Tests</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.9.0" />
|
||||
<PackageReference Include="DbMocker" Version="1.26.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Features.UserManagement\Features.UserManagement.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -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<IUserAccountRepository>();
|
||||
var handler = new GetAllUsersHandler(repoMock.Object);
|
||||
repoMock.Setup(r => r.GetAllAsync(10, 5)).ReturnsAsync(Array.Empty<UserAccount>());
|
||||
|
||||
var result = await handler.Handle(new GetAllUsersQuery(10, 5), CancellationToken.None);
|
||||
|
||||
result.Should().BeEmpty();
|
||||
repoMock.Verify(r => r.GetAllAsync(10, 5), Times.Once);
|
||||
}
|
||||
}
|
||||
@@ -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<IUserAccountRepository> _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<NotFoundException>();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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));
|
||||
@@ -0,0 +1,10 @@
|
||||
using Domain.Entities;
|
||||
using MediatR;
|
||||
|
||||
namespace Features.UserManagement.Commands.UpdateUser;
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing user account. Not currently exposed via any HTTP route, carried forward
|
||||
/// from <c>IUserService.UpdateAsync</c> as-is.
|
||||
/// </summary>
|
||||
public record UpdateUserCommand(UserAccount UserAccount) : IRequest;
|
||||
@@ -0,0 +1,15 @@
|
||||
using Features.UserManagement.Repository;
|
||||
using MediatR;
|
||||
|
||||
namespace Features.UserManagement.Commands.UpdateUser;
|
||||
|
||||
/// <summary>
|
||||
/// Handles <see cref="UpdateUserCommand"/> by persisting changes to an existing user account.
|
||||
/// </summary>
|
||||
/// <param name="repository">Repository used to persist the updated user account.</param>
|
||||
public class UpdateUserHandler(IUserAccountRepository repository)
|
||||
: IRequestHandler<UpdateUserCommand>
|
||||
{
|
||||
public Task Handle(UpdateUserCommand request, CancellationToken cancellationToken) =>
|
||||
repository.UpdateAsync(request.UserAccount);
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides read-only endpoints for retrieving user accounts.
|
||||
/// </summary>
|
||||
/// <param name="userService">Service used to query user account data.</param>
|
||||
/// <param name="mediator">Used to dispatch user queries to their handlers.</param>
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class UserController(IUserService userService) : ControllerBase
|
||||
public class UserController(IMediator mediator) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 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<ActionResult<UserAccount>> GetById(Guid id)
|
||||
{
|
||||
var user = await userService.GetByIdAsync(id);
|
||||
var user = await mediator.Send(new GetUserByIdQuery(id));
|
||||
return Ok(user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Features.UserManagement.Repository;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Features.UserManagement.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Registers the services owned by the UserManagement feature slice.
|
||||
/// </summary>
|
||||
public static class FeaturesUserManagementServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddFeaturesUserManagement(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IUserAccountRepository, UserAccountRepository>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Features.UserManagement</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Domain\Domain.Entities\Domain.Entities.csproj" />
|
||||
<ProjectReference Include="..\..\Domain\Domain.Exceptions\Domain.Exceptions.csproj" />
|
||||
<ProjectReference Include="..\..\Infrastructure\Infrastructure.Sql\Infrastructure.Sql.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\Shared.Contracts\Shared.Contracts.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\Shared.Application\Shared.Application.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,16 @@
|
||||
using Domain.Entities;
|
||||
using Features.UserManagement.Repository;
|
||||
using MediatR;
|
||||
|
||||
namespace Features.UserManagement.Queries.GetAllUsers;
|
||||
|
||||
/// <summary>
|
||||
/// Handles <see cref="GetAllUsersQuery"/> by retrieving a paginated list of user accounts.
|
||||
/// </summary>
|
||||
/// <param name="repository">Repository used to query user account data.</param>
|
||||
public class GetAllUsersHandler(IUserAccountRepository repository)
|
||||
: IRequestHandler<GetAllUsersQuery, IEnumerable<UserAccount>>
|
||||
{
|
||||
public Task<IEnumerable<UserAccount>> Handle(GetAllUsersQuery request, CancellationToken cancellationToken) =>
|
||||
repository.GetAllAsync(request.Limit, request.Offset);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Domain.Entities;
|
||||
using MediatR;
|
||||
|
||||
namespace Features.UserManagement.Queries.GetAllUsers;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a paginated list of user accounts.
|
||||
/// </summary>
|
||||
public record GetAllUsersQuery(int? Limit, int? Offset) : IRequest<IEnumerable<UserAccount>>;
|
||||
@@ -0,0 +1,23 @@
|
||||
using Domain.Entities;
|
||||
using Domain.Exceptions;
|
||||
using Features.UserManagement.Repository;
|
||||
using MediatR;
|
||||
|
||||
namespace Features.UserManagement.Queries.GetUserById;
|
||||
|
||||
/// <summary>
|
||||
/// Handles <see cref="GetUserByIdQuery"/> by looking up the matching user account.
|
||||
/// </summary>
|
||||
/// <param name="repository">Repository used to query user account data.</param>
|
||||
public class GetUserByIdHandler(IUserAccountRepository repository)
|
||||
: IRequestHandler<GetUserByIdQuery, UserAccount>
|
||||
{
|
||||
/// <exception cref="NotFoundException">Thrown when no user account exists with the given ID.</exception>
|
||||
public async Task<UserAccount> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Domain.Entities;
|
||||
using MediatR;
|
||||
|
||||
namespace Features.UserManagement.Queries.GetUserById;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a single user account by its unique identifier.
|
||||
/// </summary>
|
||||
public record GetUserByIdQuery(Guid UserAccountId) : IRequest<UserAccount>;
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Infrastructure.Repository.UserAccount;
|
||||
namespace Features.UserManagement.Repository;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for CRUD operations on user account records.
|
||||
@@ -2,7 +2,7 @@ using System.Data;
|
||||
using System.Data.Common;
|
||||
using Infrastructure.Sql;
|
||||
|
||||
namespace Infrastructure.Repository.UserAccount;
|
||||
namespace Features.UserManagement.Repository;
|
||||
|
||||
/// <summary>
|
||||
/// ADO.NET-based implementation of <see cref="IUserAccountRepository"/> backed by SQL Server
|
||||
@@ -10,7 +10,7 @@ namespace Infrastructure.Repository.UserAccount;
|
||||
/// </summary>
|
||||
/// <param name="connectionFactory">The factory used to create database connections.</param>
|
||||
public class UserAccountRepository(ISqlConnectionFactory connectionFactory)
|
||||
: Repository<Domain.Entities.UserAccount>(connectionFactory),
|
||||
: Infrastructure.Sql.Repository<Domain.Entities.UserAccount>(connectionFactory),
|
||||
IUserAccountRepository
|
||||
{
|
||||
/// <summary>
|
||||
@@ -1,13 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Domain\Domain.Exceptions\Domain.Exceptions.csproj" />
|
||||
<ProjectReference
|
||||
Include="..\..\Infrastructure\Infrastructure.Repository\Infrastructure.Repository.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,34 +0,0 @@
|
||||
using Domain.Entities;
|
||||
|
||||
namespace Service.UserManagement.User;
|
||||
|
||||
/// <summary>
|
||||
/// Defines operations for retrieving and updating user accounts.
|
||||
/// </summary>
|
||||
public interface IUserService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all user accounts, optionally paginated.
|
||||
/// </summary>
|
||||
/// <param name="limit">The maximum number of results to return, or <c>null</c> for no limit.</param>
|
||||
/// <param name="offset">The number of results to skip, or <c>null</c> to start from the beginning.</param>
|
||||
/// <returns>A collection of <see cref="UserAccount"/> entities.</returns>
|
||||
Task<IEnumerable<UserAccount>> GetAllAsync(
|
||||
int? limit = null,
|
||||
int? offset = null
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a user account by its unique identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the user account.</param>
|
||||
/// <returns>The matching <see cref="UserAccount"/>.</returns>
|
||||
Task<UserAccount> GetByIdAsync(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing user account.
|
||||
/// </summary>
|
||||
/// <param name="userAccount">The user account containing the updated data.</param>
|
||||
/// <returns>A task that completes once the update has finished.</returns>
|
||||
Task UpdateAsync(UserAccount userAccount);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using Domain.Entities;
|
||||
using Domain.Exceptions;
|
||||
using Infrastructure.Repository.UserAccount;
|
||||
|
||||
namespace Service.UserManagement.User;
|
||||
|
||||
/// <summary>
|
||||
/// Handles retrieval and update of user accounts.
|
||||
/// </summary>
|
||||
/// <param name="repository">Repository used to persist and query user account data.</param>
|
||||
public class UserService(IUserAccountRepository repository) : IUserService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all user accounts, optionally paginated.
|
||||
/// </summary>
|
||||
/// <param name="limit">The maximum number of results to return, or <c>null</c> for no limit.</param>
|
||||
/// <param name="offset">The number of results to skip, or <c>null</c> to start from the beginning.</param>
|
||||
/// <returns>A collection of <see cref="UserAccount"/> entities.</returns>
|
||||
public async Task<IEnumerable<UserAccount>> GetAllAsync(
|
||||
int? limit = null,
|
||||
int? offset = null
|
||||
)
|
||||
{
|
||||
return await repository.GetAllAsync(limit, offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a user account by its unique identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the user account.</param>
|
||||
/// <returns>The matching <see cref="UserAccount"/>.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when no user account exists with the given <paramref name="id"/>.</exception>
|
||||
public async Task<UserAccount> GetByIdAsync(Guid id)
|
||||
{
|
||||
var user = await repository.GetByIdAsync(id);
|
||||
if (user is null)
|
||||
throw new NotFoundException($"User with ID {id} not found");
|
||||
return user;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing user account.
|
||||
/// </summary>
|
||||
/// <param name="userAccount">The user account containing the updated data.</param>
|
||||
/// <returns>A task that completes once the update has finished.</returns>
|
||||
public async Task UpdateAsync(UserAccount userAccount)
|
||||
{
|
||||
await repository.UpdateAsync(userAccount);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user