Skip to content

Commit

Permalink
Create global PlatformPlatform solution and extract reusable backend …
Browse files Browse the repository at this point in the history
…foundation (#113)

### Summary & Motivation

This pull request establishes a new top-level PlatformPlatform .NET
solution, replacing the former AccountManagement solution. It extracts a
reusable backend-foundation from account management. The previous
Foundation project is divided into three sub-projects: `DomainModeling`,
`InfrastructureCore`, and `AspNetCoreUtils`. This division ensures that
dependencies don't leak into other areas.

These projects introduce a meaningful namespace structure that includes
`Entities`, `Persistence`, `Validation`, `Identity`, `Cqrs`,
`DomainEvents`, etc.

Several minor refactorings have been performed to enhance the overall
clarity of the solution. For example, the creation of a
`RepositoryBase<T, TId>` removes the boiler code needed to create a
concrete repository like TenantRepository. Furthermore, service
registration now utilizes a fluent API for greater readability and
efficiency.

### Checklist
- [x] I have added a Label to the pull-request
- [x] I have added tests, and done manual regression tests
- [x] I have updated the documentation, if necessary
  • Loading branch information
tjementum committed May 19, 2023
2 parents ebb807c + ac3606a commit 0f344c5
Show file tree
Hide file tree
Showing 96 changed files with 417 additions and 339 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Account Management"
name: "PlatformPlatform"

on:
push:
Expand All @@ -23,11 +23,9 @@ jobs:
echo "Generated version: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Restore dependencies
working-directory: account-management
run: dotnet restore AccountManagement.sln
run: dotnet restore PlatformPlatform.sln
- name: Build
working-directory: account-management
run: dotnet build AccountManagement.sln --no-restore --configuration Release /p:Version=$VERSION
run: dotnet build PlatformPlatform.sln --no-restore --configuration Release /p:Version=$VERSION

test-with-code-coverage:
needs: build
Expand All @@ -47,13 +45,12 @@ jobs:
- name: Install SonarScanner
run: dotnet tool install --global dotnet-sonarscanner
- name: Run Test with dotCover and SonarScanner reporting
working-directory: account-management
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
dotnet sonarscanner begin /k:"${{ vars.SONAR_PROJECT_KEY }}" /o:"${{ vars.SONAR_ORGANIZATION }}" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.dotcover.reportsPaths="coverage/dotCover.html"
dotnet dotcover test AccountManagement.sln --dcOutput="coverage/dotCover.html" --dcReportType=HTML
dotnet dotcover test PlatformPlatform.sln --dcOutput="coverage/dotCover.html" --dcReportType=HTML
dotnet sonarscanner end /d:sonar.login="${SONAR_TOKEN}"
jetbrains-code-inspection:
Expand All @@ -66,7 +63,7 @@ jobs:
- name: Run code inspections
uses: muno92/resharper_inspectcode@1.6.13
with:
solutionPath: account-management/AccountManagement.sln
solutionPath: PlatformPlatform.sln
minimumSeverity: warning
# Ignore cases where property getters are not called directly (e.g., on DTOs that are serialized)
ignoreIssueType: UnusedAutoPropertyAccessor.Global
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ _ReSharper*/
*.DotSettings.user

# Rider
*/.idea
.idea

# TeamCity is a build add-in
_TeamCity*
Expand Down
79 changes: 79 additions & 0 deletions PlatformPlatform.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "backend-foundation", "backend-foundation", "{F01E4DC8-2A8B-4CB9-893A-B3B8FF2EFE22}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DomainModeling", "backend-foundation\DomainModeling\DomainModeling.csproj", "{9781DA05-517E-46D7-9F8B-2C01AA74F1E4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfrastructureCore", "backend-foundation\InfrastructureCore\InfrastructureCore.csproj", "{2B5B1AC7-7254-4527-B0F0-8F0A203CB625}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreUtils", "backend-foundation\AspNetCoreUtils\AspNetCoreUtils.csproj", "{836ABCEF-C16B-4331-B8D5-0EA75E4101FE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "backend-foundation\Tests\Tests.csproj", "{B232D7BB-396A-46FE-B6D2-95041412C835}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "account-management", "account-management", "{EEA1463E-71F8-44E6-8055-787DAA5E62D2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain", "account-management\Domain\Domain.csproj", "{32C5BCAB-FC18-4C32-B008-E0FD125C5E98}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "account-management\Application\Application.csproj", "{B155D1D9-4F6E-42AE-A8F0-92CD50BF182D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "account-management\Infrastructure\Infrastructure.csproj", "{CB9BAD74-4362-4646-B653-3CB5FCAC74BD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi", "account-management\WebApi\WebApi.csproj", "{4AF4767C-1BF5-4B86-9050-F05ABF6AF4BE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "account-management\Tests\Tests.csproj", "{ABEB4337-3606-4730-8ABE-94DF98C2C348}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9781DA05-517E-46D7-9F8B-2C01AA74F1E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9781DA05-517E-46D7-9F8B-2C01AA74F1E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9781DA05-517E-46D7-9F8B-2C01AA74F1E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9781DA05-517E-46D7-9F8B-2C01AA74F1E4}.Release|Any CPU.Build.0 = Release|Any CPU
{2B5B1AC7-7254-4527-B0F0-8F0A203CB625}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B5B1AC7-7254-4527-B0F0-8F0A203CB625}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B5B1AC7-7254-4527-B0F0-8F0A203CB625}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B5B1AC7-7254-4527-B0F0-8F0A203CB625}.Release|Any CPU.Build.0 = Release|Any CPU
{836ABCEF-C16B-4331-B8D5-0EA75E4101FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{836ABCEF-C16B-4331-B8D5-0EA75E4101FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{836ABCEF-C16B-4331-B8D5-0EA75E4101FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{836ABCEF-C16B-4331-B8D5-0EA75E4101FE}.Release|Any CPU.Build.0 = Release|Any CPU
{B232D7BB-396A-46FE-B6D2-95041412C835}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B232D7BB-396A-46FE-B6D2-95041412C835}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B232D7BB-396A-46FE-B6D2-95041412C835}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B232D7BB-396A-46FE-B6D2-95041412C835}.Release|Any CPU.Build.0 = Release|Any CPU
{32C5BCAB-FC18-4C32-B008-E0FD125C5E98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32C5BCAB-FC18-4C32-B008-E0FD125C5E98}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32C5BCAB-FC18-4C32-B008-E0FD125C5E98}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32C5BCAB-FC18-4C32-B008-E0FD125C5E98}.Release|Any CPU.Build.0 = Release|Any CPU
{B155D1D9-4F6E-42AE-A8F0-92CD50BF182D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B155D1D9-4F6E-42AE-A8F0-92CD50BF182D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B155D1D9-4F6E-42AE-A8F0-92CD50BF182D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B155D1D9-4F6E-42AE-A8F0-92CD50BF182D}.Release|Any CPU.Build.0 = Release|Any CPU
{CB9BAD74-4362-4646-B653-3CB5FCAC74BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CB9BAD74-4362-4646-B653-3CB5FCAC74BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CB9BAD74-4362-4646-B653-3CB5FCAC74BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CB9BAD74-4362-4646-B653-3CB5FCAC74BD}.Release|Any CPU.Build.0 = Release|Any CPU
{4AF4767C-1BF5-4B86-9050-F05ABF6AF4BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4AF4767C-1BF5-4B86-9050-F05ABF6AF4BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AF4767C-1BF5-4B86-9050-F05ABF6AF4BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AF4767C-1BF5-4B86-9050-F05ABF6AF4BE}.Release|Any CPU.Build.0 = Release|Any CPU
{ABEB4337-3606-4730-8ABE-94DF98C2C348}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ABEB4337-3606-4730-8ABE-94DF98C2C348}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ABEB4337-3606-4730-8ABE-94DF98C2C348}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ABEB4337-3606-4730-8ABE-94DF98C2C348}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{9781DA05-517E-46D7-9F8B-2C01AA74F1E4} = {F01E4DC8-2A8B-4CB9-893A-B3B8FF2EFE22}
{2B5B1AC7-7254-4527-B0F0-8F0A203CB625} = {F01E4DC8-2A8B-4CB9-893A-B3B8FF2EFE22}
{836ABCEF-C16B-4331-B8D5-0EA75E4101FE} = {F01E4DC8-2A8B-4CB9-893A-B3B8FF2EFE22}
{B232D7BB-396A-46FE-B6D2-95041412C835} = {F01E4DC8-2A8B-4CB9-893A-B3B8FF2EFE22}
{32C5BCAB-FC18-4C32-B008-E0FD125C5E98} = {EEA1463E-71F8-44E6-8055-787DAA5E62D2}
{B155D1D9-4F6E-42AE-A8F0-92CD50BF182D} = {EEA1463E-71F8-44E6-8055-787DAA5E62D2}
{CB9BAD74-4362-4646-B653-3CB5FCAC74BD} = {EEA1463E-71F8-44E6-8055-787DAA5E62D2}
{4AF4767C-1BF5-4B86-9050-F05ABF6AF4BE} = {EEA1463E-71F8-44E6-8055-787DAA5E62D2}
{ABEB4337-3606-4730-8ABE-94DF98C2C348} = {EEA1463E-71F8-44E6-8055-787DAA5E62D2}
EndGlobalSection
EndGlobal
65 changes: 0 additions & 65 deletions account-management/AccountManagement.sln

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using PlatformPlatform.AccountManagement.Application.Tenants.Dtos;
using PlatformPlatform.Foundation.DomainModeling;

namespace PlatformPlatform.AccountManagement.Application;

Expand All @@ -14,9 +15,10 @@ public static class ApplicationConfiguration

public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
services.AddMediatR(configuration => configuration.RegisterServicesFromAssemblies(Assembly));

ConfigureMappings();
services
.AddDomainModelingServices()
.ConfigureMappings()
.AddMediatR(configuration => configuration.RegisterServicesFromAssemblies(Assembly));

return services;
}
Expand All @@ -26,8 +28,10 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
/// convention-based mapping, which means no configuration is needed if properties are named the same in both
/// the DTO and the Entity. However, it can be configured to use explicit mappings.
/// </summary>
private static void ConfigureMappings()
private static IServiceCollection ConfigureMappings(this IServiceCollection services)
{
TenantDto.ConfigureTenantDtoMapping();

return services;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
using MediatR;
using PlatformPlatform.AccountManagement.Application.Tenants.Dtos;
using PlatformPlatform.AccountManagement.Domain.Tenants;
using PlatformPlatform.Foundation.Application;
using PlatformPlatform.Foundation.Domain;
using PlatformPlatform.Foundation.DomainModeling.Cqrs;
using PlatformPlatform.Foundation.DomainModeling.Validation;

namespace PlatformPlatform.AccountManagement.Application.Tenants.Commands.CreateTenant;

Expand Down Expand Up @@ -48,11 +48,11 @@ public async Task<CommandResult<TenantDto>> Handle(CreateTenantCommand command,
return tenant.Adapt<TenantDto>();
}

private async Task<Result> IsSubdomainUniqueAsync(string subdomain, CancellationToken cancellationToken)
private async Task<ValidationResult> IsSubdomainUniqueAsync(string subdomain, CancellationToken cancellationToken)
{
var isSubdomainUnique = await _tenantRepository.IsSubdomainFreeAsync(subdomain, cancellationToken);
return isSubdomainUnique
? Result.Success()
: Result.Failure(nameof(Tenant.Subdomain), "The subdomain must be unique.");
? ValidationResult.Success()
: ValidationResult.Failure(nameof(Tenant.Subdomain), "The subdomain must be unique.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
using MediatR;
using PlatformPlatform.AccountManagement.Application.Tenants.Dtos;
using PlatformPlatform.AccountManagement.Domain.Tenants;
using PlatformPlatform.Foundation.Application;
using PlatformPlatform.Foundation.Domain;
using PlatformPlatform.Foundation.DomainModeling.Cqrs;
using PlatformPlatform.Foundation.DomainModeling.Validation;

namespace PlatformPlatform.AccountManagement.Application.Tenants.Commands.DeleteTenant;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
using MediatR;
using PlatformPlatform.AccountManagement.Application.Tenants.Dtos;
using PlatformPlatform.AccountManagement.Domain.Tenants;
using PlatformPlatform.Foundation.Application;
using PlatformPlatform.Foundation.Domain;
using PlatformPlatform.Foundation.DomainModeling.Cqrs;
using PlatformPlatform.Foundation.DomainModeling.Validation;

namespace PlatformPlatform.AccountManagement.Application.Tenants.Commands.UpdateTenant;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using MediatR;
using PlatformPlatform.AccountManagement.Application.Tenants.Dtos;
using PlatformPlatform.AccountManagement.Domain.Tenants;
using PlatformPlatform.Foundation.Application;
using PlatformPlatform.Foundation.DomainModeling.Cqrs;

namespace PlatformPlatform.AccountManagement.Application.Tenants.Queries;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Foundation\Foundation.csproj"/>
<InternalsVisibleTo Include="PlatformPlatform.AccountManagement.Tests"/>
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="PlatformPlatform.AccountManagement.Tests"/>
<ProjectReference Include="..\..\backend-foundation\DomainModeling\DomainModeling.csproj"/>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using PlatformPlatform.Foundation.Domain;
using PlatformPlatform.Foundation.DomainModeling.Persistence;

namespace PlatformPlatform.AccountManagement.Domain.Tenants;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using PlatformPlatform.Foundation.Domain;
using PlatformPlatform.Foundation.DomainModeling.Entities;
using PlatformPlatform.Foundation.DomainModeling.Identity;

namespace PlatformPlatform.AccountManagement.Domain.Tenants;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using JetBrains.Annotations;
using PlatformPlatform.Foundation.Domain;
using PlatformPlatform.Foundation.DomainModeling.DomainEvents;

namespace PlatformPlatform.AccountManagement.Domain.Tenants;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
using PlatformPlatform.Foundation.Domain;
using PlatformPlatform.Foundation.DomainModeling.Validation;

namespace PlatformPlatform.AccountManagement.Domain.Tenants;

public static class TenantValidation
{
public static Result ValidateName(string input)
public static ValidationResult ValidateName(string input)
{
const string errorMessage = "Name must be between 1 and 30 characters.";
return ValidationUtils.IsStringValid(nameof(Tenant.Name), input, 1, 30, errorMessage);
}

public static Result ValidateSubdomain(string input)
public static ValidationResult ValidateSubdomain(string input)
{
const string errorMessage = "Subdomains should be 3 to 30 lowercase alphanumeric characters.";
return ValidationUtils.IsStringValid(nameof(Tenant.Subdomain), input, "^[a-z0-9]*$", 3, 30, errorMessage);
}

public static Result ValidateEmail(string input)
public static ValidationResult ValidateEmail(string input)
{
return ValidationUtils.IsValidEmail(nameof(Tenant.Email), input);
}

public static Result ValidatePhone(string? input)
public static ValidationResult ValidatePhone(string? input)
{
return ValidationUtils.IsValidPhone(nameof(Tenant.Phone), input);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore;
using PlatformPlatform.AccountManagement.Domain.Tenants;
using PlatformPlatform.Foundation.Domain;
using PlatformPlatform.Foundation.Infrastructure;
using PlatformPlatform.Foundation.DomainModeling.Entities;
using PlatformPlatform.Foundation.InfrastructureCore.EntityFramework;

namespace PlatformPlatform.AccountManagement.Infrastructure;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\backend-foundation\InfrastructureCore\InfrastructureCore.csproj"/>
<ProjectReference Include="..\Application\Application.csproj"/>
</ItemGroup>

Expand Down
Loading

0 comments on commit 0f344c5

Please sign in to comment.