Skip to content

Commit d8ebd9e

Browse files
feature: this commit introduces the bounded context responsible for managing profiles in the comanda ecosystem.
1 parent 33caed6 commit d8ebd9e

110 files changed

Lines changed: 3963 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
MONGO_INITDB_ROOT_USERNAME=admin
2+
MONGO_INITDB_ROOT_PASSWORD=admin
3+
4+
SEQ_FIRSTRUN_ADMINPASSWORD=admin
5+
6+
MONGO_CONNECTIONSTRING=mongodb://admin:admin@vinder.comanda.profiles.infrastructure.mongo.database:27017/profiles?authSource=admin
7+
MONGO_DATABASENAME=profiles
8+
9+
ADMIN_USERNAME=admin
10+
ADMIN_PASSWORD=admin
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{B8EFCA5F-814F-285C-A8CB-F00F14650265}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Comanda.Profiles.Domain", "Source\Comanda.Profiles.Domain\Comanda.Profiles.Domain.csproj", "{C7293B3C-E854-443D-8BB8-D79A81380162}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Comanda.Profiles.Application", "Source\Comanda.Profiles.Application\Comanda.Profiles.Application.csproj", "{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Comanda.Profiles.Infrastructure", "Source\Comanda.Profiles.Infrastructure\Comanda.Profiles.Infrastructure.csproj", "{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Comanda.Profiles.Infrastructure.IoC", "Source\Comanda.Profiles.Infrastructure.IoC\Comanda.Profiles.Infrastructure.IoC.csproj", "{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}"
15+
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Comanda.Profiles.WebApi", "Source\Comanda.Profiles.WebApi\Comanda.Profiles.WebApi.csproj", "{37A746D8-CD9D-449A-AB1B-2C02F5366694}"
17+
EndProject
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Comanda.Profiles.CrossCutting", "Source\Comanda.Profiles.CrossCutting\Comanda.Profiles.CrossCutting.csproj", "{F218FF09-497E-4E48-A960-2BE2A0FD38F1}"
19+
EndProject
20+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
21+
EndProject
22+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Comanda.Profiles.TestSuite", "Tests\Comanda.Profiles.TestSuite.csproj", "{8434AF6E-FEDA-4692-A448-720429C00800}"
23+
EndProject
24+
Global
25+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
26+
Debug|Any CPU = Debug|Any CPU
27+
Debug|x64 = Debug|x64
28+
Debug|x86 = Debug|x86
29+
Release|Any CPU = Release|Any CPU
30+
Release|x64 = Release|x64
31+
Release|x86 = Release|x86
32+
EndGlobalSection
33+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
34+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Debug|Any CPU.Build.0 = Debug|Any CPU
36+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Debug|x64.ActiveCfg = Debug|Any CPU
37+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Debug|x64.Build.0 = Debug|Any CPU
38+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Debug|x86.ActiveCfg = Debug|Any CPU
39+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Debug|x86.Build.0 = Debug|Any CPU
40+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Release|Any CPU.ActiveCfg = Release|Any CPU
41+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Release|Any CPU.Build.0 = Release|Any CPU
42+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Release|x64.ActiveCfg = Release|Any CPU
43+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Release|x64.Build.0 = Release|Any CPU
44+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Release|x86.ActiveCfg = Release|Any CPU
45+
{C7293B3C-E854-443D-8BB8-D79A81380162}.Release|x86.Build.0 = Release|Any CPU
46+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
48+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Debug|x64.ActiveCfg = Debug|Any CPU
49+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Debug|x64.Build.0 = Debug|Any CPU
50+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Debug|x86.ActiveCfg = Debug|Any CPU
51+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Debug|x86.Build.0 = Debug|Any CPU
52+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
53+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Release|Any CPU.Build.0 = Release|Any CPU
54+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Release|x64.ActiveCfg = Release|Any CPU
55+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Release|x64.Build.0 = Release|Any CPU
56+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Release|x86.ActiveCfg = Release|Any CPU
57+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF}.Release|x86.Build.0 = Release|Any CPU
58+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
59+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
60+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Debug|x64.ActiveCfg = Debug|Any CPU
61+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Debug|x64.Build.0 = Debug|Any CPU
62+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Debug|x86.ActiveCfg = Debug|Any CPU
63+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Debug|x86.Build.0 = Debug|Any CPU
64+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
65+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Release|Any CPU.Build.0 = Release|Any CPU
66+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Release|x64.ActiveCfg = Release|Any CPU
67+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Release|x64.Build.0 = Release|Any CPU
68+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Release|x86.ActiveCfg = Release|Any CPU
69+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3}.Release|x86.Build.0 = Release|Any CPU
70+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
71+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Debug|Any CPU.Build.0 = Debug|Any CPU
72+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Debug|x64.ActiveCfg = Debug|Any CPU
73+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Debug|x64.Build.0 = Debug|Any CPU
74+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Debug|x86.ActiveCfg = Debug|Any CPU
75+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Debug|x86.Build.0 = Debug|Any CPU
76+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Release|Any CPU.ActiveCfg = Release|Any CPU
77+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Release|Any CPU.Build.0 = Release|Any CPU
78+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Release|x64.ActiveCfg = Release|Any CPU
79+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Release|x64.Build.0 = Release|Any CPU
80+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Release|x86.ActiveCfg = Release|Any CPU
81+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14}.Release|x86.Build.0 = Release|Any CPU
82+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
83+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Debug|Any CPU.Build.0 = Debug|Any CPU
84+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Debug|x64.ActiveCfg = Debug|Any CPU
85+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Debug|x64.Build.0 = Debug|Any CPU
86+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Debug|x86.ActiveCfg = Debug|Any CPU
87+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Debug|x86.Build.0 = Debug|Any CPU
88+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Release|Any CPU.ActiveCfg = Release|Any CPU
89+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Release|Any CPU.Build.0 = Release|Any CPU
90+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Release|x64.ActiveCfg = Release|Any CPU
91+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Release|x64.Build.0 = Release|Any CPU
92+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Release|x86.ActiveCfg = Release|Any CPU
93+
{37A746D8-CD9D-449A-AB1B-2C02F5366694}.Release|x86.Build.0 = Release|Any CPU
94+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
95+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
96+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Debug|x64.ActiveCfg = Debug|Any CPU
97+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Debug|x64.Build.0 = Debug|Any CPU
98+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Debug|x86.ActiveCfg = Debug|Any CPU
99+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Debug|x86.Build.0 = Debug|Any CPU
100+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
101+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Release|Any CPU.Build.0 = Release|Any CPU
102+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Release|x64.ActiveCfg = Release|Any CPU
103+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Release|x64.Build.0 = Release|Any CPU
104+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Release|x86.ActiveCfg = Release|Any CPU
105+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1}.Release|x86.Build.0 = Release|Any CPU
106+
{8434AF6E-FEDA-4692-A448-720429C00800}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
107+
{8434AF6E-FEDA-4692-A448-720429C00800}.Debug|Any CPU.Build.0 = Debug|Any CPU
108+
{8434AF6E-FEDA-4692-A448-720429C00800}.Debug|x64.ActiveCfg = Debug|Any CPU
109+
{8434AF6E-FEDA-4692-A448-720429C00800}.Debug|x64.Build.0 = Debug|Any CPU
110+
{8434AF6E-FEDA-4692-A448-720429C00800}.Debug|x86.ActiveCfg = Debug|Any CPU
111+
{8434AF6E-FEDA-4692-A448-720429C00800}.Debug|x86.Build.0 = Debug|Any CPU
112+
{8434AF6E-FEDA-4692-A448-720429C00800}.Release|Any CPU.ActiveCfg = Release|Any CPU
113+
{8434AF6E-FEDA-4692-A448-720429C00800}.Release|Any CPU.Build.0 = Release|Any CPU
114+
{8434AF6E-FEDA-4692-A448-720429C00800}.Release|x64.ActiveCfg = Release|Any CPU
115+
{8434AF6E-FEDA-4692-A448-720429C00800}.Release|x64.Build.0 = Release|Any CPU
116+
{8434AF6E-FEDA-4692-A448-720429C00800}.Release|x86.ActiveCfg = Release|Any CPU
117+
{8434AF6E-FEDA-4692-A448-720429C00800}.Release|x86.Build.0 = Release|Any CPU
118+
EndGlobalSection
119+
GlobalSection(SolutionProperties) = preSolution
120+
HideSolutionNode = FALSE
121+
EndGlobalSection
122+
GlobalSection(NestedProjects) = preSolution
123+
{C7293B3C-E854-443D-8BB8-D79A81380162} = {B8EFCA5F-814F-285C-A8CB-F00F14650265}
124+
{5B4760C0-F3BD-4837-8D1B-19E59A13BDDF} = {B8EFCA5F-814F-285C-A8CB-F00F14650265}
125+
{31C332BD-1EA3-4A7B-B737-BCB46C78AFF3} = {B8EFCA5F-814F-285C-A8CB-F00F14650265}
126+
{329A734D-2607-40FD-9ED2-3BF7ABEC2A14} = {B8EFCA5F-814F-285C-A8CB-F00F14650265}
127+
{37A746D8-CD9D-449A-AB1B-2C02F5366694} = {B8EFCA5F-814F-285C-A8CB-F00F14650265}
128+
{F218FF09-497E-4E48-A960-2BE2A0FD38F1} = {B8EFCA5F-814F-285C-A8CB-F00F14650265}
129+
{8434AF6E-FEDA-4692-A448-720429C00800} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
130+
EndGlobalSection
131+
EndGlobal
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# use ASP.NET Core 9.0 runtime image as base
2+
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
3+
WORKDIR /app
4+
EXPOSE 8080
5+
6+
# use SDK image for build
7+
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
8+
WORKDIR /src
9+
10+
# copy project files to restore dependencies
11+
COPY ["Source/Comanda.Profiles.WebApi/Comanda.Profiles.WebApi.csproj", "Comanda.Profiles.WebApi/"]
12+
13+
# copy the entire solution 'n related projects
14+
COPY ["Comanda.Profiles.sln", "./"]
15+
16+
# restore dependencies for the project
17+
RUN dotnet restore "Comanda.Profiles.WebApi/Comanda.Profiles.WebApi.csproj"
18+
19+
# copy all source code into the container
20+
COPY Source/ ./Source/
21+
22+
# set working directory to the web project
23+
WORKDIR "/src/Source/Comanda.Profiles.WebApi"
24+
25+
# build in Release mode
26+
RUN dotnet build "Comanda.Profiles.WebApi.csproj" -c Release -o /app/build
27+
28+
# publish the project for production
29+
FROM build AS publish
30+
RUN dotnet publish "Comanda.Profiles.WebApi.csproj" -c Release -o /app/publish /p:UseAppHost=false
31+
32+
# final image to run the app
33+
FROM base AS final
34+
WORKDIR /app
35+
36+
# copy published files from the publish stage
37+
COPY --from=publish /app/publish .
38+
39+
# set the command to start the application
40+
ENTRYPOINT ["dotnet", "Comanda.Profiles.WebApi.dll"]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="FluentValidation" Version="12.0.0" />
11+
<PackageReference Include="HttpsRichardy.Dispatcher" Version="1.0.2" />
12+
<PackageReference Include="HttpsRichardy.Federation.Sdk.Contracts" Version="1.0.2" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="../Comanda.Profiles.Domain/Comanda.Profiles.Domain.csproj" />
17+
</ItemGroup>
18+
19+
</Project>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
namespace Comanda.Profiles.Application.Handlers.Customer;
2+
3+
public sealed class AssignCustomerAddressHandler(ICustomerCollection collection) :
4+
IDispatchHandler<AssignCustomerAddressScheme, Result<Address>>
5+
{
6+
public async Task<Result<Address>> HandleAsync(
7+
AssignCustomerAddressScheme parameters, CancellationToken cancellation = default)
8+
{
9+
var filters = CustomerFilters.WithSpecifications()
10+
.WithIdentifier(parameters.CustomerId)
11+
.Build();
12+
13+
var customers = await collection.GetCustomersAsync(filters, cancellation);
14+
var customer = customers.FirstOrDefault();
15+
16+
var address = parameters.AsAddress();
17+
18+
if (customer is null)
19+
{
20+
/* for tracking purposes: raise error #COMANDA-ERROR-AF04C */
21+
/* this allows us to quickly locate all occurrences of this error in the codebase. */
22+
23+
return Result<Address>.Failure(CustomerErrors.CustomerDoesNotExist);
24+
}
25+
26+
/* we compare addresses by value (using the record's Equals) instead of by reference as with a regular class. */
27+
28+
if (customer.Addresses.Any(existing => existing.Equals(address)))
29+
{
30+
/* for tracking purposes: raise error #COMANDA-ERROR-4901F */
31+
/* this allows us to quickly locate all occurrences of this error in the codebase. */
32+
33+
return Result<Address>.Failure(CustomerErrors.AddressAlreadyAssigned);
34+
}
35+
36+
customer.Addresses.Add(address);
37+
38+
await collection.UpdateAsync(customer, cancellation);
39+
40+
return Result<Address>.Success(address);
41+
}
42+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
namespace Comanda.Profiles.Application.Handlers.Customer;
2+
3+
public sealed class CustomerCreationHandler(ICustomerCollection collection, IActivityCollection activityCollection) :
4+
IDispatchHandler<CustomerCreationScheme, Result<CustomerScheme>>
5+
{
6+
public async Task<Result<CustomerScheme>> HandleAsync(
7+
CustomerCreationScheme parameters, CancellationToken cancellation = default)
8+
{
9+
var filters = CustomerFilters.WithSpecifications()
10+
.WithEmail(parameters.Email)
11+
.Build();
12+
13+
var matchingCustomers = await collection.GetCustomersAsync(filters, cancellation);
14+
var existingCustomer = matchingCustomers.FirstOrDefault();
15+
16+
if (existingCustomer is not null)
17+
{
18+
/* for tracking purposes: raise error #COMANDA-ERROR-76A71 */
19+
/* this allows us to quickly locate all occurrences of this error in the codebase. */
20+
21+
return Result<CustomerScheme>.Failure(ProfileErrors.ProfileAlreadyExists);
22+
}
23+
24+
var customer = await collection.InsertAsync(parameters.AsCustomer(), cancellation: cancellation);
25+
var activity = new Activity
26+
{
27+
Action = "comanda.actions.customer.creation",
28+
Description = $"customer '{customer.FirstName} {customer.LastName}' created.",
29+
30+
/* we can filter activities by resource using resource property */
31+
/* therefore, always register the resource to help us track it. */
32+
33+
Resource = Resource.From(customer.Id, nameof(Customer)),
34+
Metadata = new Dictionary<string, string>
35+
{
36+
{ "customer.identifier", customer.Id },
37+
{ "customer.user.identifier", customer.User.Identifier }
38+
}
39+
};
40+
41+
await activityCollection.InsertAsync(activity, cancellation: cancellation);
42+
43+
return Result<CustomerScheme>.Success(customer.AsResponse());
44+
}
45+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace Comanda.Profiles.Application.Handlers.Customer;
2+
3+
public sealed class CustomerDeletionHandler(ICustomerCollection collection) :
4+
IDispatchHandler<CustomerDeletionScheme, Result>
5+
{
6+
public async Task<Result> HandleAsync(CustomerDeletionScheme parameters, CancellationToken cancellation = default)
7+
{
8+
var filters = CustomerFilters.WithSpecifications()
9+
.WithIdentifier(parameters.CustomerId)
10+
.WithIsDeleted(false)
11+
.Build();
12+
13+
var matchingCustomers = await collection.GetCustomersAsync(filters, cancellation);
14+
var existingCustomer = matchingCustomers.FirstOrDefault();
15+
16+
if (existingCustomer is null)
17+
{
18+
/* for tracking purposes: raise error #COMANDA-ERROR-AF04C */
19+
return Result.Failure(CustomerErrors.CustomerDoesNotExist);
20+
}
21+
22+
await collection.DeleteAsync(existingCustomer, cancellation: cancellation);
23+
24+
return Result.Success();
25+
}
26+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
namespace Comanda.Profiles.Application.Handlers.Customer;
2+
3+
public sealed class DeleteCustomerAddressHandler(ICustomerCollection collection) :
4+
IDispatchHandler<DeleteCustomerAddressScheme, Result>
5+
{
6+
public async Task<Result> HandleAsync(
7+
DeleteCustomerAddressScheme parameters, CancellationToken cancellation = default)
8+
{
9+
var filters = CustomerFilters.WithSpecifications()
10+
.WithIdentifier(parameters.CustomerId)
11+
.Build();
12+
13+
var customers = await collection.GetCustomersAsync(filters, cancellation);
14+
var customer = customers.FirstOrDefault();
15+
16+
if (customer is null)
17+
{
18+
/* for tracking purposes: raise error #COMANDA-ERROR-AF04C */
19+
return Result.Failure(CustomerErrors.CustomerDoesNotExist);
20+
}
21+
22+
var existingAddress = customer.Addresses.FirstOrDefault(address => address.Equals(parameters.Target));
23+
if (existingAddress is null)
24+
{
25+
/* for tracking purposes: raise error #COMANDA-ERROR-2616B */
26+
return Result.Failure(CustomerErrors.AddressDoesNotExist);
27+
}
28+
29+
customer.Addresses.Remove(existingAddress);
30+
31+
await collection.UpdateAsync(customer, cancellation);
32+
33+
return Result.Success();
34+
}
35+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
namespace Comanda.Profiles.Application.Handlers.Customer;
2+
3+
public sealed class EditCustomerAddressHandler(ICustomerCollection collection) :
4+
IDispatchHandler<EditCustomerAddressScheme, Result<Address>>
5+
{
6+
public async Task<Result<Address>> HandleAsync(
7+
EditCustomerAddressScheme parameters, CancellationToken cancellation = default)
8+
{
9+
var filters = CustomerFilters.WithSpecifications()
10+
.WithIdentifier(parameters.CustomerId)
11+
.Build();
12+
13+
var customers = await collection.GetCustomersAsync(filters, cancellation);
14+
var customer = customers.FirstOrDefault();
15+
16+
if (customer is null)
17+
{
18+
/* for tracking purposes: raise error #COMANDA-ERROR-AF04C */
19+
return Result<Address>.Failure(CustomerErrors.CustomerDoesNotExist);
20+
}
21+
22+
var existingAddress = customer.Addresses.FirstOrDefault(address => address.Equals(parameters.Target));
23+
if (existingAddress is null)
24+
{
25+
/* for tracking purposes: raise error #COMANDA-ERROR-2616B */
26+
return Result<Address>.Failure(CustomerErrors.AddressDoesNotExist);
27+
}
28+
29+
if (existingAddress.Equals(parameters.Replacement))
30+
{
31+
/* for tracking purposes: raise error #COMANDA-ERROR-4901F */
32+
return Result<Address>.Failure(CustomerErrors.AddressAlreadyAssigned);
33+
}
34+
35+
customer.Addresses.Remove(existingAddress);
36+
customer.Addresses.Add(parameters.Replacement);
37+
38+
await collection.UpdateAsync(customer, cancellation);
39+
40+
return Result<Address>.Success(parameters.Replacement);
41+
}
42+
}

0 commit comments

Comments
 (0)