Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: ♻️ refactor project folder structure and improve tests #11

Merged
merged 1 commit into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 37 additions & 28 deletions Vertical.Slice.Template.sln
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vertical.Slice.Template.Con
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vertical.Slice.Template.TestsShared", "tests\Vertical.Slice.Template.TestsShared\Vertical.Slice.Template.TestsShared.csproj", "{E3C9B67C-8443-49C9-A6F3-EA099199F66F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vertical.Slice.Template.Shared", "src\Vertical.Slice.Template.Shared\Vertical.Slice.Template.Shared.csproj", "{05B80842-E143-46CA-A881-CF14A85D927C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vertical.Slice.Template.ApiClient", "src\Vertical.Slice.Template.ApiClient\Vertical.Slice.Template.ApiClient.csproj", "{51C30F5B-FC3F-41C2-BD7C-FA254B956C55}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution-items", "solution-items", "{798579C1-7DEC-47A2-9C18-CA3DBE4A6573}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Expand Down Expand Up @@ -72,10 +68,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
.github\workflows\publish.yml = .github\workflows\publish.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vertical.Slice.Template", "src\Vertical.Slice.Template\Vertical.Slice.Template.csproj", "{31F7A4C7-66DD-4387-9981-4A64501018E7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vertical.Slice.Template.Api", "src\Vertical.Slice.Template.Api\Vertical.Slice.Template.Api.csproj", "{F45584B2-2831-410B-BC9D-3E13817E768F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".devcontainer", ".devcontainer", "{268E2B03-D9F4-4D31-84BA-6D1DC250E894}"
ProjectSection(SolutionItems) = preProject
.devcontainer\devcontainer.json = .devcontainer\devcontainer.json
Expand All @@ -98,6 +90,20 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docker-compose", "docker-co
deployments\docker-compose\docker-compose.infrastructure.yaml = deployments\docker-compose\docker-compose.infrastructure.yaml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ApiClients", "ApiClients", "{66D6E224-01FF-4D8F-98C1-CEA3CD2D5F15}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{AF80152C-AF0D-475E-AD69-A7867D1ACA26}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{54C91A04-E128-4ADB-9B49-E0FEAC783069}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiClients", "src\ApiClients\ApiClients.csproj", "{56A79202-F0BB-4BDB-B042-B773EDBCDFE3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vertical.Slice.Template", "src\App\Vertical.Slice.Template\Vertical.Slice.Template.csproj", "{AC478225-5EA9-4895-875A-0B01217C4576}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vertical.Slice.Template.Api", "src\App\Vertical.Slice.Template.Api\Vertical.Slice.Template.Api.csproj", "{23F3C3DD-6630-4743-BE50-9BD6F719665E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "src\Shared\Shared.csproj", "{A5FB3B9E-FF0A-45F4-8D37-080C770A5AB4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -119,14 +125,17 @@ Global
{A79D441C-D450-474E-9CFC-008335A80B13} = {C4B80800-5430-4D4E-B473-261157D54CD4}
{AEFEEF63-5831-49E5-A7D9-892AF653C32D} = {798579C1-7DEC-47A2-9C18-CA3DBE4A6573}
{ED6C6F59-8A39-4D7D-BB93-888AA486AFE9} = {AEFEEF63-5831-49E5-A7D9-892AF653C32D}
{51C30F5B-FC3F-41C2-BD7C-FA254B956C55} = {9BB0311A-FE23-4C8D-B7C0-E8CD49FA3D20}
{05B80842-E143-46CA-A881-CF14A85D927C} = {9BB0311A-FE23-4C8D-B7C0-E8CD49FA3D20}
{31F7A4C7-66DD-4387-9981-4A64501018E7} = {9BB0311A-FE23-4C8D-B7C0-E8CD49FA3D20}
{F45584B2-2831-410B-BC9D-3E13817E768F} = {9BB0311A-FE23-4C8D-B7C0-E8CD49FA3D20}
{268E2B03-D9F4-4D31-84BA-6D1DC250E894} = {798579C1-7DEC-47A2-9C18-CA3DBE4A6573}
{3A68A53A-1E76-49DE-A622-569267333288} = {268E2B03-D9F4-4D31-84BA-6D1DC250E894}
{9DF99E09-AEC5-4F44-BC7E-CB16EF796431} = {798579C1-7DEC-47A2-9C18-CA3DBE4A6573}
{F3FCF437-0EF6-4149-AA20-46C787B6FCB1} = {9DF99E09-AEC5-4F44-BC7E-CB16EF796431}
{66D6E224-01FF-4D8F-98C1-CEA3CD2D5F15} = {9BB0311A-FE23-4C8D-B7C0-E8CD49FA3D20}
{AF80152C-AF0D-475E-AD69-A7867D1ACA26} = {9BB0311A-FE23-4C8D-B7C0-E8CD49FA3D20}
{54C91A04-E128-4ADB-9B49-E0FEAC783069} = {9BB0311A-FE23-4C8D-B7C0-E8CD49FA3D20}
{56A79202-F0BB-4BDB-B042-B773EDBCDFE3} = {66D6E224-01FF-4D8F-98C1-CEA3CD2D5F15}
{AC478225-5EA9-4895-875A-0B01217C4576} = {AF80152C-AF0D-475E-AD69-A7867D1ACA26}
{23F3C3DD-6630-4743-BE50-9BD6F719665E} = {AF80152C-AF0D-475E-AD69-A7867D1ACA26}
{A5FB3B9E-FF0A-45F4-8D37-080C770A5AB4} = {54C91A04-E128-4ADB-9B49-E0FEAC783069}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5F41BF51-E13B-48BF-8D81-874FBA9BC961}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
Expand All @@ -149,14 +158,6 @@ Global
{E3C9B67C-8443-49C9-A6F3-EA099199F66F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E3C9B67C-8443-49C9-A6F3-EA099199F66F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E3C9B67C-8443-49C9-A6F3-EA099199F66F}.Release|Any CPU.Build.0 = Release|Any CPU
{05B80842-E143-46CA-A881-CF14A85D927C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{05B80842-E143-46CA-A881-CF14A85D927C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{05B80842-E143-46CA-A881-CF14A85D927C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{05B80842-E143-46CA-A881-CF14A85D927C}.Release|Any CPU.Build.0 = Release|Any CPU
{51C30F5B-FC3F-41C2-BD7C-FA254B956C55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{51C30F5B-FC3F-41C2-BD7C-FA254B956C55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{51C30F5B-FC3F-41C2-BD7C-FA254B956C55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{51C30F5B-FC3F-41C2-BD7C-FA254B956C55}.Release|Any CPU.Build.0 = Release|Any CPU
{4E185D04-8B8D-4BE9-AAE3-5406C703DF83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E185D04-8B8D-4BE9-AAE3-5406C703DF83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E185D04-8B8D-4BE9-AAE3-5406C703DF83}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -165,13 +166,21 @@ Global
{A79D441C-D450-474E-9CFC-008335A80B13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A79D441C-D450-474E-9CFC-008335A80B13}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A79D441C-D450-474E-9CFC-008335A80B13}.Release|Any CPU.Build.0 = Release|Any CPU
{31F7A4C7-66DD-4387-9981-4A64501018E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{31F7A4C7-66DD-4387-9981-4A64501018E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{31F7A4C7-66DD-4387-9981-4A64501018E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{31F7A4C7-66DD-4387-9981-4A64501018E7}.Release|Any CPU.Build.0 = Release|Any CPU
{F45584B2-2831-410B-BC9D-3E13817E768F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F45584B2-2831-410B-BC9D-3E13817E768F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F45584B2-2831-410B-BC9D-3E13817E768F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F45584B2-2831-410B-BC9D-3E13817E768F}.Release|Any CPU.Build.0 = Release|Any CPU
{56A79202-F0BB-4BDB-B042-B773EDBCDFE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{56A79202-F0BB-4BDB-B042-B773EDBCDFE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{56A79202-F0BB-4BDB-B042-B773EDBCDFE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{56A79202-F0BB-4BDB-B042-B773EDBCDFE3}.Release|Any CPU.Build.0 = Release|Any CPU
{AC478225-5EA9-4895-875A-0B01217C4576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC478225-5EA9-4895-875A-0B01217C4576}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC478225-5EA9-4895-875A-0B01217C4576}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC478225-5EA9-4895-875A-0B01217C4576}.Release|Any CPU.Build.0 = Release|Any CPU
{23F3C3DD-6630-4743-BE50-9BD6F719665E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23F3C3DD-6630-4743-BE50-9BD6F719665E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23F3C3DD-6630-4743-BE50-9BD6F719665E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23F3C3DD-6630-4743-BE50-9BD6F719665E}.Release|Any CPU.Build.0 = Release|Any CPU
{A5FB3B9E-FF0A-45F4-8D37-080C770A5AB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A5FB3B9E-FF0A-45F4-8D37-080C770A5AB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5FB3B9E-FF0A-45F4-8D37-080C770A5AB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5FB3B9E-FF0A-45F4-8D37-080C770A5AB4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace Vertical.Slice.Template.Api;

public class CatalogsApiMetadata { }
public class CatalogsApiMetadata;
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using System.Reflection;
using Serilog;
using Serilog.Events;
using Shared.Core.Extensions.ServiceCollectionsExtensions;
using Shared.Logging;
using Shared.Swagger;
using Shared.Web.Minimal.Extensions;
using Vertical.Slice.Template;
using Vertical.Slice.Template.Shared;
using Vertical.Slice.Template.Shared.Core.Extensions.ServiceCollectionsExtensions;
using Vertical.Slice.Template.Shared.Extensions.WebApplicationBuilderExtensions;
using Vertical.Slice.Template.Shared.Logging;
using Vertical.Slice.Template.Shared.Swagger;
using Vertical.Slice.Template.Shared.Web.Minimal.Extensions;

// https://github.com/serilog/serilog-aspnetcore#two-stage-initialization
Log.Logger = new LoggerConfiguration()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@
<ProjectReference Include="..\Vertical.Slice.Template\Vertical.Slice.Template.csproj" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Vertical.Slice.Template.DependencyTests" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using Humanizer;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Shared.EF;
using Vertical.Slice.Template.Products.Models;
using Vertical.Slice.Template.Shared.Data;
using Vertical.Slice.Template.Shared.EF;

namespace Vertical.Slice.Template.Products.Data.Configurations;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
using FluentValidation;
using MediatR;
using Microsoft.Extensions.Logging;
using Shared.Abstractions.Core.CQRS;
using Shared.Abstractions.Persistence.Ef;
using Shared.Core.Extensions;
using Shared.Core.Id;
using Shared.EF.Extensions;
using Shared.Validation.Extensions;
using Vertical.Slice.Template.Products.Models;
using Vertical.Slice.Template.Shared.Abstractions.Core.CQRS;
using Vertical.Slice.Template.Shared.Abstractions.Ef;
using Vertical.Slice.Template.Shared.Core.Extensions;
using Vertical.Slice.Template.Shared.Core.Id;
using Vertical.Slice.Template.Shared.Data;
using Vertical.Slice.Template.Shared.EF.Extensions;
using Vertical.Slice.Template.Shared.Validation.Extensions;

namespace Vertical.Slice.Template.Products.Features.CreatingProduct.v1;

Expand All @@ -20,7 +20,7 @@ namespace Vertical.Slice.Template.Products.Features.CreatingProduct.v1;
// https://codeopinion.com/leaking-value-objects-from-your-domain/
// https://www.youtube.com/watch?v=CdanF8PWJng
// we don't pass value-objects and domains to our commands and events, just primitive types
public record CreateProduct(string Name, Guid CategoryId, decimal Price, string? Description = null)
internal record CreateProduct(string Name, Guid CategoryId, decimal Price, string? Description = null)
: ICommand<CreateProductResult>
{
public Guid Id { get; } = IdGenerator.NewId();
Expand Down Expand Up @@ -50,38 +50,25 @@ public CreateProductValidator()
}
}

internal class CreateProductHandler : ICommandHandler<CreateProduct, CreateProductResult>
internal class CreateProductHandler(
DbExecuters.CreateAndSaveProductExecutor createAndSaveProductExecutor,
IMapper mapper,
IMediator mediator,
ILogger<CreateProductHandler> logger
) : ICommandHandler<CreateProduct, CreateProductResult>
{
private readonly DbExecuters.CreateAndSaveProductExecutor _createAndSaveProductExecutor;
private readonly IMapper _mapper;
private readonly IMediator _mediator;
private readonly ILogger<CreateProductHandler> _logger;

public CreateProductHandler(
DbExecuters.CreateAndSaveProductExecutor createAndSaveProductExecutor,
IMapper mapper,
IMediator mediator,
ILogger<CreateProductHandler> logger
)
{
_createAndSaveProductExecutor = createAndSaveProductExecutor;
_mapper = mapper;
_mediator = mediator;
_logger = logger;
}

public async Task<CreateProductResult> Handle(CreateProduct request, CancellationToken cancellationToken)
{
request.NotBeNull();

var product = _mapper.Map<Product>(request);
var product = mapper.Map<Product>(request);

await _createAndSaveProductExecutor(product, cancellationToken);
await createAndSaveProductExecutor(product, cancellationToken);

var (name, categoryId, price, description) = request;
await _mediator.Publish(ProductCreated.Of(request.Id, name, categoryId, price, description), cancellationToken);
await mediator.Publish(ProductCreated.Of(request.Id, name, categoryId, price, description), cancellationToken);

_logger.LogInformation("Product a with ID: '{ProductId} created.'", request.Id);
logger.LogInformation("Product a with ID: '{ProductId} created.'", request.Id);

return new CreateProductResult(product.Id);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Vertical.Slice.Template.Shared.Abstractions.Web;
using Vertical.Slice.Template.Shared.Web.Minimal.Extensions;
using Vertical.Slice.Template.Shared.Web.ProblemDetail.HttpResults;
using Shared.Abstractions.Web;
using Shared.Web.Minimal.Extensions;
using Shared.Web.ProblemDetail.HttpResults;

namespace Vertical.Slice.Template.Products.Features.CreatingProduct.v1;

Expand Down Expand Up @@ -65,4 +65,4 @@ CancellationToken CancellationToken
internal record CreateProductResponse(Guid Id);

// we can expect any value from the user for all reference types are nullable and we should do some validation in other levels (we use pure records mostly for dtos without needing validation)
public record CreateProductRequest(string? Name, Guid CategoryId, decimal Price, string? Description);
internal record CreateProductRequest(string? Name, Guid CategoryId, decimal Price, string? Description);
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using FluentValidation;
using Vertical.Slice.Template.Shared.Core.Domain.Events;
using Vertical.Slice.Template.Shared.Validation.Extensions;
using Shared.Core.Domain.Events;
using Shared.Validation.Extensions;

namespace Vertical.Slice.Template.Products.Features.CreatingProduct.v1;

Expand All @@ -11,7 +11,7 @@ namespace Vertical.Slice.Template.Products.Features.CreatingProduct.v1;
// https://codeopinion.com/leaking-value-objects-from-your-domain/
// https://www.youtube.com/watch?v=CdanF8PWJng
// we don't pass value-objects and domains to our commands and events, just primitive types
public record ProductCreated(Guid Id, string Name, Guid CategoryId, decimal Price, string? Description = null)
internal record ProductCreated(Guid Id, string Name, Guid CategoryId, decimal Price, string? Description = null)
: DomainEvent
{
public static ProductCreated Of(Guid id, string? name, Guid categoryId, decimal price, string? description = null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
using AutoMapper;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using Shared.Abstractions.Core.CQRS;
using Shared.Abstractions.Persistence.Ef;
using Shared.Cache;
using Shared.Core.Exceptions;
using Shared.Core.Extensions;
using Shared.EF.Extensions;
using Shared.Validation.Extensions;
using Vertical.Slice.Template.Products.Dtos.v1;
using Vertical.Slice.Template.Products.Models;
using Vertical.Slice.Template.Products.ReadModel;
using Vertical.Slice.Template.Shared.Abstractions.Core.CQRS;
using Vertical.Slice.Template.Shared.Abstractions.Ef;
using Vertical.Slice.Template.Shared.Cache;
using Vertical.Slice.Template.Shared.Core.Exceptions;
using Vertical.Slice.Template.Shared.Core.Extensions;
using Vertical.Slice.Template.Shared.Data;
using Vertical.Slice.Template.Shared.EF.Extensions;
using Vertical.Slice.Template.Shared.Validation.Extensions;

namespace Vertical.Slice.Template.Products.Features.GettingProductById.v1;

public record GetProductById(Guid Id) : CacheQuery<GetProductById, GetProductByIdResult>
internal record GetProductById(Guid Id) : CacheQuery<GetProductById, GetProductByIdResult>
{
/// <summary>
/// GetProductById query with validation.
Expand All @@ -41,29 +41,21 @@ public GetProductByIdValidator()
}
}

internal class GetProductByIdHandler : IQueryHandler<GetProductById, GetProductByIdResult>
internal class GetProductByIdHandler(DbExecutors.GetProductByIdExecutor getProductByIdExecutor, IMapper mapper)
: IQueryHandler<GetProductById, GetProductByIdResult>
{
private readonly DbExecutors.GetProductByIdExecutor _getProductByIdExecutor;
private readonly IMapper _mapper;

public GetProductByIdHandler(DbExecutors.GetProductByIdExecutor getProductByIdExecutor, IMapper mapper)
{
_getProductByIdExecutor = getProductByIdExecutor;
_mapper = mapper;
}

public async Task<GetProductByIdResult> Handle(GetProductById request, CancellationToken cancellationToken)
{
request.NotBeNull();

var productReadModel = await _getProductByIdExecutor(request.Id, cancellationToken);
var productReadModel = await getProductByIdExecutor(request.Id, cancellationToken);

if (productReadModel is null)
{
throw new NotFoundException($"product with id {request.Id} not found");
}

var productDto = _mapper.Map<ProductDto>(productReadModel);
var productDto = mapper.Map<ProductDto>(productReadModel);

return new GetProductByIdResult(productDto);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Vertical.Slice.Template.Products.Dtos;
using Shared.Abstractions.Web;
using Shared.Web.Minimal.Extensions;
using Shared.Web.ProblemDetail.HttpResults;
using Vertical.Slice.Template.Products.Dtos.v1;
using Vertical.Slice.Template.Shared.Abstractions.Web;
using Vertical.Slice.Template.Shared.Web.Minimal.Extensions;
using Vertical.Slice.Template.Shared.Web.ProblemDetail.HttpResults;

namespace Vertical.Slice.Template.Products.Features.GettingProductById.v1;

Expand Down
Loading
Loading