From ae77958ad710bc301377bf38d8daef0c0ba6e242 Mon Sep 17 00:00:00 2001 From: "Daniel Mackay [SSW]" <2636640+danielmackay@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:55:01 +1000 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=2086=20replace=20automapper?= =?UTF-8?q?=20with=20manual=20mappers=20(#439)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 - ...-replace-automapper-with-manual-mapping.md | 51 +++++++++++++++++++ src/Application/Application.csproj | 1 - src/Application/DependencyInjection.cs | 1 - src/Application/GlobalUsings.cs | 3 +- .../Commands/CreateHero/CreateHeroCommand.cs | 5 +- .../Commands/CreateHero/CreateHeroPowerDto.cs | 7 --- .../Commands/UpdateHero/UpdateHeroCommand.cs | 5 +- .../Commands/UpdateHero/UpdateHeroPowerDto.cs | 7 --- .../GetAllHeroes/GetAllHeroesMapping.cs | 16 ------ .../Queries/GetAllHeroes/GetAllHeroesQuery.cs | 17 +++++-- .../Heroes/Queries/GetAllHeroes/HeroDto.cs | 16 ------ .../AddHeroToTeam/AddHeroToTeamCommand.cs | 3 +- .../CompleteMission/CompleteMissionCommand.cs | 3 +- .../Commands/CreateTeam/CreateTeamCommand.cs | 3 +- .../ExecuteMission/ExecuteMissionCommand.cs | 3 +- .../Events/PowerLevelUpdatedEventHandler.cs | 2 +- .../Queries/GetAllTeams/GetAllHeroesQuery.cs | 10 ++-- .../Queries/GetAllTeams/GetAllTeamsMapping.cs | 12 ----- .../Teams/Queries/GetAllTeams/TeamDto.cs | 7 --- .../Teams/Queries/GetTeam/GetTeamQuery.cs | 30 ++++------- .../CommandName/CommandNameCommand.cs | 18 ++++--- .../CommandNameCommandValidator.cs | 9 ---- .../query/Queries/QueryName/EntityNameDto.cs | 6 --- .../Queries/QueryName/QueryNameMapping.cs | 12 ----- .../query/Queries/QueryName/QueryNameQuery.cs | 16 ++---- .../Heroes/Commands/CreateHeroCommandTests.cs | 2 +- .../Heroes/Commands/UpdateHeroCommandTests.cs | 4 +- .../Events/UpdatePowerLevelEventTests.cs | 2 +- 29 files changed, 108 insertions(+), 164 deletions(-) create mode 100644 docs/adr/20241118-replace-automapper-with-manual-mapping.md delete mode 100644 src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroPowerDto.cs delete mode 100644 src/Application/UseCases/Heroes/Commands/UpdateHero/UpdateHeroPowerDto.cs delete mode 100644 src/Application/UseCases/Heroes/Queries/GetAllHeroes/GetAllHeroesMapping.cs delete mode 100644 src/Application/UseCases/Heroes/Queries/GetAllHeroes/HeroDto.cs delete mode 100644 src/Application/UseCases/Teams/Queries/GetAllTeams/GetAllTeamsMapping.cs delete mode 100644 src/Application/UseCases/Teams/Queries/GetAllTeams/TeamDto.cs delete mode 100644 templates/command/Commands/CommandName/CommandNameCommandValidator.cs delete mode 100644 templates/query/Queries/QueryName/EntityNameDto.cs delete mode 100644 templates/query/Queries/QueryName/QueryNameMapping.cs diff --git a/README.md b/README.md index 438003b7..6f247d8c 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,6 @@ This is a template for creating a new project using [Clean Architecture](https:/ - 📦 ErrorOr - fluent result pattern (instead of exceptions) - 📦 FluentValidation - for validating requests - as per [ssw.com.au/rules/use-fluent-validation/](https://ssw.com.au/rules/use-fluent-validation/) -- 📦 AutoMapper - for mapping between objects - 🆔 Strongly Typed IDs - to combat primitive obsession - e.g. pass `CustomerId` type into methods instead of `int`, or `Guid` - Entity Framework can automatically convert the int, Guid, nvarchar(..) to strongly typed ID. diff --git a/docs/adr/20241118-replace-automapper-with-manual-mapping.md b/docs/adr/20241118-replace-automapper-with-manual-mapping.md new file mode 100644 index 00000000..b32144a7 --- /dev/null +++ b/docs/adr/20241118-replace-automapper-with-manual-mapping.md @@ -0,0 +1,51 @@ +# Replace AutoMapper with manual mapping + +- Status: Approved +- Deciders: Daniel Mackay, Matt Goldman +- Date: 2024-11-18 +- Tags: mappers + +Technical Story: https://github.com/SSWConsulting/SSW.CleanArchitecture/issues/86 + +## Context and Problem Statement + +We currently use AutoMapper to map the output of queries in the CA Template. While saving some work, this can also lead more complicate mappers, and runtime issues due to missing fields or mappings. + +While mappers solve a problem in a certain set of cases, they can also introduce complexity and runtime issues, and are not a sensible default. + +## Decision Drivers + +- Reduce runtime errors +- Reduce tooling removing 'unused' properties + +## Considered Options + +1. AutoMapper +2. Manual Mapper + +## Decision Outcome + +Chosen option: "Option 2 - Manual Mapper", because it reduces the runtime errors, and makes both simple and complex mapping scenarios easier to understand. + +### Consequences + +- ✅ Once Automapper is removed, we can remove the mapping profiles from the code +- ✅ DTOs can now use records, making the code much simpler +- ✅ With much more concise code, we can fit everything in one file +- ✅ With everything in one file, we can remove a layer of folders + +## Pros and Cons of the Options + +### Option 1 - AutoMapper + +- ✅ Less code to write for simple mapping scenarios +- ❌ Mapping becomes complex for complicated scenarios +- ❌ Can lead to runtime issues due to missing fields or mappings +- ❌ Need to learn a new library + +### Option 2 - Manual Mapper + +- ✅ Mapping becomes simple for both simple and complicated scenarios +- ✅ Reduced runtime issues due to missing fields or mappings +- ✅ No need to learn a new library +- ❌ More code needed for mapping diff --git a/src/Application/Application.csproj b/src/Application/Application.csproj index f3ea1746..1e149428 100644 --- a/src/Application/Application.csproj +++ b/src/Application/Application.csproj @@ -8,7 +8,6 @@ - diff --git a/src/Application/DependencyInjection.cs b/src/Application/DependencyInjection.cs index b8877175..af982731 100644 --- a/src/Application/DependencyInjection.cs +++ b/src/Application/DependencyInjection.cs @@ -9,7 +9,6 @@ public static IServiceCollection AddApplication(this IServiceCollection services { var applicationAssembly = typeof(DependencyInjection).Assembly; - services.AddAutoMapper(applicationAssembly); services.AddValidatorsFromAssembly(applicationAssembly); services.AddMediatR(config => diff --git a/src/Application/GlobalUsings.cs b/src/Application/GlobalUsings.cs index 67da9dff..5bc18c07 100644 --- a/src/Application/GlobalUsings.cs +++ b/src/Application/GlobalUsings.cs @@ -1,5 +1,4 @@ -global using AutoMapper; -global using FluentValidation; +global using FluentValidation; global using MediatR; global using Ardalis.Specification.EntityFrameworkCore; global using ErrorOr; diff --git a/src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroCommand.cs b/src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroCommand.cs index 0b8bb357..61746a7a 100644 --- a/src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroCommand.cs +++ b/src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroCommand.cs @@ -8,8 +8,9 @@ public sealed record CreateHeroCommand( string Alias, IEnumerable Powers) : IRequest>; -// ReSharper disable once UnusedType.Global -public sealed class CreateHeroCommandHandler(IApplicationDbContext dbContext) +public record CreateHeroPowerDto(string Name, int PowerLevel); + +internal sealed class CreateHeroCommandHandler(IApplicationDbContext dbContext) : IRequestHandler> { public async Task> Handle(CreateHeroCommand request, CancellationToken cancellationToken) diff --git a/src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroPowerDto.cs b/src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroPowerDto.cs deleted file mode 100644 index 02df59ef..00000000 --- a/src/Application/UseCases/Heroes/Commands/CreateHero/CreateHeroPowerDto.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace SSW.CleanArchitecture.Application.UseCases.Heroes.Commands.CreateHero; - -public class CreateHeroPowerDto -{ - public required string Name { get; set; } - public required int PowerLevel { get; set; } -} \ No newline at end of file diff --git a/src/Application/UseCases/Heroes/Commands/UpdateHero/UpdateHeroCommand.cs b/src/Application/UseCases/Heroes/Commands/UpdateHero/UpdateHeroCommand.cs index 717afc9d..0fc73372 100644 --- a/src/Application/UseCases/Heroes/Commands/UpdateHero/UpdateHeroCommand.cs +++ b/src/Application/UseCases/Heroes/Commands/UpdateHero/UpdateHeroCommand.cs @@ -13,8 +13,9 @@ public sealed record UpdateHeroCommand( public Guid HeroId { get; set; } } -// ReSharper disable once UnusedType.Global -public sealed class UpdateHeroCommandHandler(IApplicationDbContext dbContext) +public record UpdateHeroPowerDto(string Name, int PowerLevel); + +internal sealed class UpdateHeroCommandHandler(IApplicationDbContext dbContext) : IRequestHandler> { public async Task> Handle(UpdateHeroCommand request, CancellationToken cancellationToken) diff --git a/src/Application/UseCases/Heroes/Commands/UpdateHero/UpdateHeroPowerDto.cs b/src/Application/UseCases/Heroes/Commands/UpdateHero/UpdateHeroPowerDto.cs deleted file mode 100644 index 7f1f7af8..00000000 --- a/src/Application/UseCases/Heroes/Commands/UpdateHero/UpdateHeroPowerDto.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace SSW.CleanArchitecture.Application.UseCases.Heroes.Commands.UpdateHero; - -public class UpdateHeroPowerDto -{ - public required string Name { get; set; } - public required int PowerLevel { get; set; } -} \ No newline at end of file diff --git a/src/Application/UseCases/Heroes/Queries/GetAllHeroes/GetAllHeroesMapping.cs b/src/Application/UseCases/Heroes/Queries/GetAllHeroes/GetAllHeroesMapping.cs deleted file mode 100644 index d40a44af..00000000 --- a/src/Application/UseCases/Heroes/Queries/GetAllHeroes/GetAllHeroesMapping.cs +++ /dev/null @@ -1,16 +0,0 @@ -using SSW.CleanArchitecture.Domain.Heroes; - -namespace SSW.CleanArchitecture.Application.UseCases.Heroes.Queries.GetAllHeroes; - -public class GetAllHeroesMapping : Profile -{ - public GetAllHeroesMapping() - { - CreateMap() - .ForMember(d => d.Id, opt => opt.MapFrom(s => s.Id.Value)); - - CreateMap() - .ForMember(d => d.Name, opt => opt.MapFrom(s => s.Name)) - .ForMember(d => d.PowerLevel, opt => opt.MapFrom(s => s.PowerLevel)); - } -} \ No newline at end of file diff --git a/src/Application/UseCases/Heroes/Queries/GetAllHeroes/GetAllHeroesQuery.cs b/src/Application/UseCases/Heroes/Queries/GetAllHeroes/GetAllHeroesQuery.cs index e3ff87c9..44c3ed08 100644 --- a/src/Application/UseCases/Heroes/Queries/GetAllHeroes/GetAllHeroesQuery.cs +++ b/src/Application/UseCases/Heroes/Queries/GetAllHeroes/GetAllHeroesQuery.cs @@ -1,20 +1,27 @@ -using AutoMapper.QueryableExtensions; using SSW.CleanArchitecture.Application.Common.Interfaces; namespace SSW.CleanArchitecture.Application.UseCases.Heroes.Queries.GetAllHeroes; public record GetAllHeroesQuery : IRequest>; -public sealed class GetAllHeroesQueryHandler( - IMapper mapper, - IApplicationDbContext dbContext) : IRequestHandler> +public record HeroDto(Guid Id, string Name, string Alias, int PowerLevel, IEnumerable Powers); + +public record HeroPowerDto(string Name, int PowerLevel); + +internal sealed class GetAllHeroesQueryHandler(IApplicationDbContext dbContext) + : IRequestHandler> { public async Task> Handle( GetAllHeroesQuery request, CancellationToken cancellationToken) { return await dbContext.Heroes - .ProjectTo(mapper.ConfigurationProvider) + .Select(h => new HeroDto( + h.Id.Value, + h.Name, + h.Alias, + h.PowerLevel, + h.Powers.Select(p => new HeroPowerDto(p.Name, p.PowerLevel)))) .ToListAsync(cancellationToken); } } \ No newline at end of file diff --git a/src/Application/UseCases/Heroes/Queries/GetAllHeroes/HeroDto.cs b/src/Application/UseCases/Heroes/Queries/GetAllHeroes/HeroDto.cs deleted file mode 100644 index 47f71c2a..00000000 --- a/src/Application/UseCases/Heroes/Queries/GetAllHeroes/HeroDto.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace SSW.CleanArchitecture.Application.UseCases.Heroes.Queries.GetAllHeroes; - -public class HeroDto -{ - public Guid Id { get; set; } - public required string Name { get; set; } - public required string Alias { get; set; } - public int PowerLevel { get; set; } - public IEnumerable Powers { get; set; } = []; -} - -public class HeroPowerDto -{ - public required string Name { get; set; } - public int PowerLevel { get; set; } -} \ No newline at end of file diff --git a/src/Application/UseCases/Teams/Commands/AddHeroToTeam/AddHeroToTeamCommand.cs b/src/Application/UseCases/Teams/Commands/AddHeroToTeam/AddHeroToTeamCommand.cs index f1b79926..edb79173 100644 --- a/src/Application/UseCases/Teams/Commands/AddHeroToTeam/AddHeroToTeamCommand.cs +++ b/src/Application/UseCases/Teams/Commands/AddHeroToTeam/AddHeroToTeamCommand.cs @@ -6,8 +6,7 @@ namespace SSW.CleanArchitecture.Application.UseCases.Teams.Commands.AddHeroToTea public sealed record AddHeroToTeamCommand(Guid TeamId, Guid HeroId) : IRequest>; -// ReSharper disable once UnusedType.Global -public sealed class AddHeroToTeamCommandHandler(IApplicationDbContext dbContext) +internal sealed class AddHeroToTeamCommandHandler(IApplicationDbContext dbContext) : IRequestHandler> { public async Task> Handle(AddHeroToTeamCommand request, CancellationToken cancellationToken) diff --git a/src/Application/UseCases/Teams/Commands/CompleteMission/CompleteMissionCommand.cs b/src/Application/UseCases/Teams/Commands/CompleteMission/CompleteMissionCommand.cs index ab9f9d7c..814c7b98 100644 --- a/src/Application/UseCases/Teams/Commands/CompleteMission/CompleteMissionCommand.cs +++ b/src/Application/UseCases/Teams/Commands/CompleteMission/CompleteMissionCommand.cs @@ -5,8 +5,7 @@ namespace SSW.CleanArchitecture.Application.UseCases.Teams.Commands.CompleteMiss public sealed record CompleteMissionCommand(Guid TeamId) : IRequest>; -// ReSharper disable once UnusedType.Global -public sealed class CompleteMissionCommandHandler(IApplicationDbContext dbContext) +internal sealed class CompleteMissionCommandHandler(IApplicationDbContext dbContext) : IRequestHandler> { public async Task> Handle(CompleteMissionCommand request, CancellationToken cancellationToken) diff --git a/src/Application/UseCases/Teams/Commands/CreateTeam/CreateTeamCommand.cs b/src/Application/UseCases/Teams/Commands/CreateTeam/CreateTeamCommand.cs index 33898db7..1b1b7d01 100644 --- a/src/Application/UseCases/Teams/Commands/CreateTeam/CreateTeamCommand.cs +++ b/src/Application/UseCases/Teams/Commands/CreateTeam/CreateTeamCommand.cs @@ -5,8 +5,7 @@ namespace SSW.CleanArchitecture.Application.UseCases.Teams.Commands.CreateTeam; public sealed record CreateTeamCommand(string Name) : IRequest>; -// ReSharper disable once UnusedType.Global -public sealed class CreateTeamCommandHandler(IApplicationDbContext dbContext) +internal sealed class CreateTeamCommandHandler(IApplicationDbContext dbContext) : IRequestHandler> { public async Task> Handle(CreateTeamCommand request, CancellationToken cancellationToken) diff --git a/src/Application/UseCases/Teams/Commands/ExecuteMission/ExecuteMissionCommand.cs b/src/Application/UseCases/Teams/Commands/ExecuteMission/ExecuteMissionCommand.cs index 4d07c142..110854e2 100644 --- a/src/Application/UseCases/Teams/Commands/ExecuteMission/ExecuteMissionCommand.cs +++ b/src/Application/UseCases/Teams/Commands/ExecuteMission/ExecuteMissionCommand.cs @@ -9,8 +9,7 @@ public sealed record ExecuteMissionCommand(string Description) : IRequest> { public async Task> Handle(ExecuteMissionCommand request, CancellationToken cancellationToken) diff --git a/src/Application/UseCases/Teams/Events/PowerLevelUpdatedEventHandler.cs b/src/Application/UseCases/Teams/Events/PowerLevelUpdatedEventHandler.cs index 82f7b901..a3881b0f 100644 --- a/src/Application/UseCases/Teams/Events/PowerLevelUpdatedEventHandler.cs +++ b/src/Application/UseCases/Teams/Events/PowerLevelUpdatedEventHandler.cs @@ -6,7 +6,7 @@ namespace SSW.CleanArchitecture.Application.UseCases.Teams.Events; -public class PowerLevelUpdatedEventHandler( +internal sealed class PowerLevelUpdatedEventHandler( IApplicationDbContext dbContext, ILogger logger) : INotificationHandler diff --git a/src/Application/UseCases/Teams/Queries/GetAllTeams/GetAllHeroesQuery.cs b/src/Application/UseCases/Teams/Queries/GetAllTeams/GetAllHeroesQuery.cs index 3b24728e..f1baba94 100644 --- a/src/Application/UseCases/Teams/Queries/GetAllTeams/GetAllHeroesQuery.cs +++ b/src/Application/UseCases/Teams/Queries/GetAllTeams/GetAllHeroesQuery.cs @@ -1,20 +1,20 @@ -using AutoMapper.QueryableExtensions; using SSW.CleanArchitecture.Application.Common.Interfaces; namespace SSW.CleanArchitecture.Application.UseCases.Teams.Queries.GetAllTeams; public record GetAllTeamsQuery : IRequest>; -public sealed class GetAllTeamsQueryHandler( - IMapper mapper, - IApplicationDbContext dbContext) : IRequestHandler> +public record TeamDto(Guid Id, string Name); + +internal sealed class GetAllTeamsQueryHandler(IApplicationDbContext dbContext) + : IRequestHandler> { public async Task> Handle( GetAllTeamsQuery request, CancellationToken cancellationToken) { return await dbContext.Teams - .ProjectTo(mapper.ConfigurationProvider) + .Select(t => new TeamDto(t.Id.Value, t.Name)) .ToListAsync(cancellationToken); } } \ No newline at end of file diff --git a/src/Application/UseCases/Teams/Queries/GetAllTeams/GetAllTeamsMapping.cs b/src/Application/UseCases/Teams/Queries/GetAllTeams/GetAllTeamsMapping.cs deleted file mode 100644 index 87c5e011..00000000 --- a/src/Application/UseCases/Teams/Queries/GetAllTeams/GetAllTeamsMapping.cs +++ /dev/null @@ -1,12 +0,0 @@ -using SSW.CleanArchitecture.Domain.Teams; - -namespace SSW.CleanArchitecture.Application.UseCases.Teams.Queries.GetAllTeams; - -public class GetAllTeamsMapping : Profile -{ - public GetAllTeamsMapping() - { - CreateMap() - .ForMember(d => d.Id, opt => opt.MapFrom(s => s.Id.Value)); - } -} \ No newline at end of file diff --git a/src/Application/UseCases/Teams/Queries/GetAllTeams/TeamDto.cs b/src/Application/UseCases/Teams/Queries/GetAllTeams/TeamDto.cs deleted file mode 100644 index e105516d..00000000 --- a/src/Application/UseCases/Teams/Queries/GetAllTeams/TeamDto.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace SSW.CleanArchitecture.Application.UseCases.Teams.Queries.GetAllTeams; - -public class TeamDto -{ - public Guid Id { get; init; } - public required string Name { get; init; } -} \ No newline at end of file diff --git a/src/Application/UseCases/Teams/Queries/GetTeam/GetTeamQuery.cs b/src/Application/UseCases/Teams/Queries/GetTeam/GetTeamQuery.cs index ed9e9322..ad85e7a8 100644 --- a/src/Application/UseCases/Teams/Queries/GetTeam/GetTeamQuery.cs +++ b/src/Application/UseCases/Teams/Queries/GetTeam/GetTeamQuery.cs @@ -5,7 +5,12 @@ namespace SSW.CleanArchitecture.Application.UseCases.Teams.Queries.GetTeam; public record GetTeamQuery(Guid TeamId) : IRequest>; -public sealed class GetAllTeamsQueryHandler(IApplicationDbContext dbContext) : IRequestHandler> +public record TeamDto(Guid Id, string Name, IEnumerable Heroes); + +public record HeroDto(Guid Id, string Name); + +internal sealed class GetAllTeamsQueryHandler(IApplicationDbContext dbContext) + : IRequestHandler> { public async Task> Handle( GetTeamQuery request, @@ -15,12 +20,10 @@ public async Task> Handle( var team = await dbContext.Teams .Where(t => t.Id == teamId) - .Select(t => new TeamDto - { - Id = t.Id.Value, - Name = t.Name, - Heroes = t.Heroes.Select(h => new HeroDto { Id = h.Id.Value, Name = h.Name }).ToList() - }) + .Select(t => new TeamDto( + t.Id.Value, + t.Name, + t.Heroes.Select(h => new HeroDto(h.Id.Value, h.Name)))) .FirstOrDefaultAsync(cancellationToken); if (team is null) @@ -28,17 +31,4 @@ public async Task> Handle( return team; } -} - -public class TeamDto -{ - public Guid Id { get; init; } - public required string Name { get; init; } - public List Heroes { get; init; } = []; -} - -public class HeroDto -{ - public Guid Id { get; init; } - public required string Name { get; init; } } \ No newline at end of file diff --git a/templates/command/Commands/CommandName/CommandNameCommand.cs b/templates/command/Commands/CommandName/CommandNameCommand.cs index 479b018b..7898070c 100644 --- a/templates/command/Commands/CommandName/CommandNameCommand.cs +++ b/templates/command/Commands/CommandName/CommandNameCommand.cs @@ -4,19 +4,21 @@ namespace SSW.CleanArchitecture.Application.UseCases.EntityNames.Commands.Comman public record CommandNameCommand() : IRequest>; -public class CommandNameCommandHandler : IRequestHandler> +internal sealed class CommandNameCommandHandler(IApplicationDbContext dbContext) + : IRequestHandler> { - private readonly IApplicationDbContext _dbContext; - - public CommandNameCommandHandler(IApplicationDbContext dbContext) - { - _dbContext = dbContext; - } - public async Task> Handle(CommandNameCommand request, CancellationToken cancellationToken) { // TODO: Add your business logic and persistence here throw new NotImplementedException(); } +} + +public class CommandNameCommandValidator : AbstractValidator +{ + public CommandNameCommandValidator() + { + // TODO: Add your validation rules here. For example: RuleFor(p => p.Foo).NotEmpty() + } } \ No newline at end of file diff --git a/templates/command/Commands/CommandName/CommandNameCommandValidator.cs b/templates/command/Commands/CommandName/CommandNameCommandValidator.cs deleted file mode 100644 index 2f108a77..00000000 --- a/templates/command/Commands/CommandName/CommandNameCommandValidator.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SSW.CleanArchitecture.Application.UseCases.EntityNames.Commands.CommandName; - -public class CommandNameCommandValidator : AbstractValidator -{ - public CommandNameCommandValidator() - { - // TODO: Add your validation rules here. For example: RuleFor(p => p.Foo).NotEmpty() - } -} \ No newline at end of file diff --git a/templates/query/Queries/QueryName/EntityNameDto.cs b/templates/query/Queries/QueryName/EntityNameDto.cs deleted file mode 100644 index 99b9721c..00000000 --- a/templates/query/Queries/QueryName/EntityNameDto.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace SSW.CleanArchitecture.Application.UseCases.EntityNames.Queries.QueryName; - -public class EntityNameDto -{ - // TODO: Add properties here -} diff --git a/templates/query/Queries/QueryName/QueryNameMapping.cs b/templates/query/Queries/QueryName/QueryNameMapping.cs deleted file mode 100644 index 4eb446f2..00000000 --- a/templates/query/Queries/QueryName/QueryNameMapping.cs +++ /dev/null @@ -1,12 +0,0 @@ -using SSW.CleanArchitecture.Domain.Entities; - -namespace SSW.CleanArchitecture.Application.UseCases.EntityNames.Queries.QueryName; - -public class QueryNameMapping : Profile -{ - public QueryNameMapping() - { - CreateMap() - .ForMember(d => d.Id, opt => opt.MapFrom(s => s.Id.Value)); - } -} diff --git a/templates/query/Queries/QueryName/QueryNameQuery.cs b/templates/query/Queries/QueryName/QueryNameQuery.cs index f6f9d492..447ca330 100644 --- a/templates/query/Queries/QueryName/QueryNameQuery.cs +++ b/templates/query/Queries/QueryName/QueryNameQuery.cs @@ -5,19 +5,11 @@ namespace SSW.CleanArchitecture.Application.UseCases.EntityNames.Queries.QueryNa public record QueryNameQuery : IRequest>; -public class QueryNameQueryHandler : IRequestHandler> -{ - private readonly IMapper _mapper; - private readonly IApplicationDbContext _dbContext; - - public QueryNameQueryHandler( - IMapper mapper, - IApplicationDbContext dbContext) - { - _mapper = mapper; - _dbContext = dbContext; - } +public record EntityNameDto(/* Add properties here */); +internal sealed class QueryNameQueryHandler(IApplicationDbContext dbContext) + : IRequestHandler> +{ public async Task> Handle( QueryNameQuery request, CancellationToken cancellationToken) diff --git a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs index ae65965e..07bd2ce7 100644 --- a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs +++ b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/CreateHeroCommandTests.cs @@ -22,7 +22,7 @@ public async Task Command_ShouldCreateHero() var cmd = new CreateHeroCommand( "Clark Kent", "Superman", - powers.Select(p => new CreateHeroPowerDto { Name = p.Name, PowerLevel = p.PowerLevel })); + powers.Select(p => new CreateHeroPowerDto(p.Name, p.PowerLevel))); var client = GetAnonymousClient(); // Act diff --git a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs index 4276552c..da46346a 100644 --- a/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs +++ b/tests/WebApi.IntegrationTests/Endpoints/Heroes/Commands/UpdateHeroCommandTests.cs @@ -29,7 +29,7 @@ public async Task Command_ShouldUpdateHero() var cmd = new UpdateHeroCommand( heroName, heroAlias, - powers.Select(p => new UpdateHeroPowerDto { Name = p.Name, PowerLevel = p.PowerLevel })); + powers.Select(p => new UpdateHeroPowerDto(p.Name, p.PowerLevel))); cmd.HeroId = hero.Id.Value; var client = GetAnonymousClient(); var createdTimeStamp = DateTime.Now; @@ -58,7 +58,7 @@ public async Task Command_WhenHeroDoesNotExist_ShouldReturnNotFound() var cmd = new UpdateHeroCommand( "foo", "bar", - [new UpdateHeroPowerDto { Name = "Heat vision", PowerLevel = 7 }]); + [new UpdateHeroPowerDto("Heat vision", 7)]); cmd.HeroId = heroId.Value; var client = GetAnonymousClient(); diff --git a/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs b/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs index 0909b8a0..75275a1b 100644 --- a/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs +++ b/tests/WebApi.IntegrationTests/Endpoints/Teams/Events/UpdatePowerLevelEventTests.cs @@ -25,7 +25,7 @@ public async Task Command_UpdatePowerOnTeam() Context.Teams.Add(team); await Context.SaveChangesAsync(); powers.Add(new Power("Speed", 5)); - var powerDtos = powers.Select(p => new UpdateHeroPowerDto { Name = p.Name, PowerLevel = p.PowerLevel }); + var powerDtos = powers.Select(p => new UpdateHeroPowerDto(p.Name, p.PowerLevel)); var cmd = new UpdateHeroCommand(hero.Name, hero.Alias, powerDtos); cmd.HeroId = hero.Id.Value; var client = GetAnonymousClient();