mirror of
https://github.com/alex289/CleanArchitecture.git
synced 2025-08-22 19:28:34 +00:00
Merge pull request #26 from alex289/feature/rabbitmq
feat: Add rabbitmq
This commit is contained in:
commit
f8e9ad741c
@ -15,8 +15,8 @@ namespace CleanArchitecture.Api.BackgroundServices;
|
|||||||
|
|
||||||
public sealed class SetInactiveUsersService : BackgroundService
|
public sealed class SetInactiveUsersService : BackgroundService
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider _serviceProvider;
|
|
||||||
private readonly ILogger<SetInactiveUsersService> _logger;
|
private readonly ILogger<SetInactiveUsersService> _logger;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
public SetInactiveUsersService(
|
public SetInactiveUsersService(
|
||||||
IServiceProvider serviceProvider,
|
IServiceProvider serviceProvider,
|
||||||
@ -55,7 +55,7 @@ public sealed class SetInactiveUsersService : BackgroundService
|
|||||||
{
|
{
|
||||||
user.SetInactive();
|
user.SetInactive();
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await context.SaveChangesAsync(stoppingToken);
|
await context.SaveChangesAsync(stoppingToken);
|
||||||
@ -64,7 +64,7 @@ public sealed class SetInactiveUsersService : BackgroundService
|
|||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error while setting users to inactive");
|
_logger.LogError(ex, "Error while setting users to inactive");
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(TimeSpan.FromDays(1), stoppingToken);
|
await Task.Delay(TimeSpan.FromDays(1), stoppingToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,28 +7,29 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.ApplicationStatus" Version="7.0.0" />
|
<PackageReference Include="AspNetCore.HealthChecks.ApplicationStatus" Version="7.0.0"/>
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="7.0.0" />
|
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="7.0.0"/>
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="7.0.0" />
|
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="7.0.0"/>
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="7.1.0" />
|
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="7.0.0"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.10" />
|
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="7.1.0"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.10" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.10"/>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="7.0.10" />
|
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.10" />
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.10"/>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -3,6 +3,7 @@ using CleanArchitecture.Api.Extensions;
|
|||||||
using CleanArchitecture.Application.Extensions;
|
using CleanArchitecture.Application.Extensions;
|
||||||
using CleanArchitecture.Application.gRPC;
|
using CleanArchitecture.Application.gRPC;
|
||||||
using CleanArchitecture.Domain.Extensions;
|
using CleanArchitecture.Domain.Extensions;
|
||||||
|
using CleanArchitecture.Domain.Rabbitmq.Extensions;
|
||||||
using CleanArchitecture.Infrastructure.Database;
|
using CleanArchitecture.Infrastructure.Database;
|
||||||
using CleanArchitecture.Infrastructure.Extensions;
|
using CleanArchitecture.Infrastructure.Extensions;
|
||||||
using HealthChecks.ApplicationStatus.DependencyInjection;
|
using HealthChecks.ApplicationStatus.DependencyInjection;
|
||||||
@ -14,7 +15,6 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using StackExchange.Redis;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@ -29,10 +29,17 @@ builder.Services
|
|||||||
|
|
||||||
if (builder.Environment.IsProduction())
|
if (builder.Environment.IsProduction())
|
||||||
{
|
{
|
||||||
|
var rabbitHost = builder.Configuration["RabbitMQ:Host"];
|
||||||
|
var rabbitUser = builder.Configuration["RabbitMQ:Username"];
|
||||||
|
var rabbitPass = builder.Configuration["RabbitMQ:Password"];
|
||||||
|
|
||||||
builder.Services
|
builder.Services
|
||||||
.AddHealthChecks()
|
.AddHealthChecks()
|
||||||
.AddSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")!)
|
.AddSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")!)
|
||||||
.AddRedis(builder.Configuration["RedisHostName"]!, "Redis");
|
.AddRedis(builder.Configuration["RedisHostName"]!, "Redis")
|
||||||
|
.AddRabbitMQ(
|
||||||
|
rabbitConnectionString: $"amqp://{rabbitUser}:{rabbitPass}@{rabbitHost}",
|
||||||
|
name: "RabbitMQ");
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
@ -51,6 +58,8 @@ builder.Services.AddCommandHandlers();
|
|||||||
builder.Services.AddNotificationHandlers();
|
builder.Services.AddNotificationHandlers();
|
||||||
builder.Services.AddApiUser();
|
builder.Services.AddApiUser();
|
||||||
|
|
||||||
|
builder.Services.AddRabbitMqHandler(builder.Configuration, "RabbitMQ");
|
||||||
|
|
||||||
builder.Services.AddHostedService<SetInactiveUsersService>();
|
builder.Services.AddHostedService<SetInactiveUsersService>();
|
||||||
|
|
||||||
builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly); });
|
builder.Services.AddMediatR(cfg => { cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly); });
|
||||||
|
@ -12,5 +12,11 @@
|
|||||||
"Issuer": "CleanArchitectureServer",
|
"Issuer": "CleanArchitectureServer",
|
||||||
"Audience": "CleanArchitectureClient",
|
"Audience": "CleanArchitectureClient",
|
||||||
"Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*"
|
"Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*"
|
||||||
|
},
|
||||||
|
"RabbitMQ": {
|
||||||
|
"Host": "localhost",
|
||||||
|
"Username": "guest",
|
||||||
|
"Password": "guest",
|
||||||
|
"Enabled": "True"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
CleanArchitecture.Api/appsettings.Integration.json
Normal file
14
CleanArchitecture.Api/appsettings.Integration.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"RabbitMQ": {
|
||||||
|
"Host": "localhost",
|
||||||
|
"Username": "guest",
|
||||||
|
"Password": "guest",
|
||||||
|
"Enabled": "False"
|
||||||
|
}
|
||||||
|
}
|
@ -14,5 +14,11 @@
|
|||||||
"Audience": "CleanArchitectureClient",
|
"Audience": "CleanArchitectureClient",
|
||||||
"Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*"
|
"Secret": "sD3v061gf8BxXgmxcHssasjdlkasjd87439284)@#(*"
|
||||||
},
|
},
|
||||||
"RedisHostName": "redis"
|
"RedisHostName": "redis",
|
||||||
|
"RabbitMQ": {
|
||||||
|
"Host": "rabbitmq",
|
||||||
|
"Username": "admin",
|
||||||
|
"Password": "DOIA9234JF",
|
||||||
|
"Enabled": "True"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2"/>
|
||||||
<PackageReference Include="MockQueryable.NSubstitute" Version="7.0.0" />
|
<PackageReference Include="MockQueryable.NSubstitute" Version="7.0.0"/>
|
||||||
<PackageReference Include="NSubstitute" Version="5.0.0" />
|
<PackageReference Include="NSubstitute" Version="5.0.0"/>
|
||||||
<PackageReference Include="xunit" Version="2.5.0" />
|
<PackageReference Include="xunit" Version="2.5.0"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -24,8 +24,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -32,7 +32,7 @@ public sealed class GetAllTenantsQueryHandlerTests
|
|||||||
result.PageSize.Should().Be(query.PageSize);
|
result.PageSize.Should().Be(query.PageSize);
|
||||||
result.Page.Should().Be(query.Page);
|
result.Page.Should().Be(query.Page);
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
|
|
||||||
tenant.Should().BeEquivalentTo(result.Items.First());
|
tenant.Should().BeEquivalentTo(result.Items.First());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ public sealed class GetAllTenantsQueryHandlerTests
|
|||||||
public async Task Should_Not_Get_Deleted_Tenant()
|
public async Task Should_Not_Get_Deleted_Tenant()
|
||||||
{
|
{
|
||||||
_fixture.SetupTenant(true);
|
_fixture.SetupTenant(true);
|
||||||
|
|
||||||
var query = new PageQuery
|
var query = new PageQuery
|
||||||
{
|
{
|
||||||
PageSize = 10,
|
PageSize = 10,
|
||||||
@ -50,7 +50,7 @@ public sealed class GetAllTenantsQueryHandlerTests
|
|||||||
var result = await _fixture.QueryHandler.Handle(
|
var result = await _fixture.QueryHandler.Handle(
|
||||||
new GetAllTenantsQuery(query),
|
new GetAllTenantsQuery(query),
|
||||||
default);
|
default);
|
||||||
|
|
||||||
result.PageSize.Should().Be(query.PageSize);
|
result.PageSize.Should().Be(query.PageSize);
|
||||||
result.Page.Should().Be(query.Page);
|
result.Page.Should().Be(query.Page);
|
||||||
result.Count.Should().Be(0);
|
result.Count.Should().Be(0);
|
||||||
|
@ -16,7 +16,7 @@ public sealed class GetAllUsersQueryHandlerTests
|
|||||||
public async Task Should_Get_All_Users()
|
public async Task Should_Get_All_Users()
|
||||||
{
|
{
|
||||||
var user = _fixture.SetupUserAsync();
|
var user = _fixture.SetupUserAsync();
|
||||||
|
|
||||||
var query = new PageQuery
|
var query = new PageQuery
|
||||||
{
|
{
|
||||||
PageSize = 1,
|
PageSize = 1,
|
||||||
@ -28,7 +28,7 @@ public sealed class GetAllUsersQueryHandlerTests
|
|||||||
default);
|
default);
|
||||||
|
|
||||||
_fixture.VerifyNoDomainNotification();
|
_fixture.VerifyNoDomainNotification();
|
||||||
|
|
||||||
result.PageSize.Should().Be(query.PageSize);
|
result.PageSize.Should().Be(query.PageSize);
|
||||||
result.Page.Should().Be(query.Page);
|
result.Page.Should().Be(query.Page);
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
@ -43,7 +43,7 @@ public sealed class GetAllUsersQueryHandlerTests
|
|||||||
public async Task Should_Not_Get_Deleted_Users()
|
public async Task Should_Not_Get_Deleted_Users()
|
||||||
{
|
{
|
||||||
_fixture.SetupDeletedUserAsync();
|
_fixture.SetupDeletedUserAsync();
|
||||||
|
|
||||||
var query = new PageQuery
|
var query = new PageQuery
|
||||||
{
|
{
|
||||||
PageSize = 10,
|
PageSize = 10,
|
||||||
@ -55,7 +55,7 @@ public sealed class GetAllUsersQueryHandlerTests
|
|||||||
default);
|
default);
|
||||||
|
|
||||||
_fixture.VerifyNoDomainNotification();
|
_fixture.VerifyNoDomainNotification();
|
||||||
|
|
||||||
result.PageSize.Should().Be(query.PageSize);
|
result.PageSize.Should().Be(query.PageSize);
|
||||||
result.Page.Should().Be(query.Page);
|
result.Page.Should().Be(query.Page);
|
||||||
result.Count.Should().Be(0);
|
result.Count.Should().Be(0);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Application.ViewModels;
|
using CleanArchitecture.Application.ViewModels;
|
||||||
using CleanArchitecture.Application.ViewModels.Users;
|
using CleanArchitecture.Application.ViewModels.Users;
|
||||||
|
@ -27,21 +27,21 @@ public sealed class GetAllTenantsQueryHandler :
|
|||||||
.GetAllNoTracking()
|
.GetAllNoTracking()
|
||||||
.Include(x => x.Users)
|
.Include(x => x.Users)
|
||||||
.Where(x => !x.Deleted);
|
.Where(x => !x.Deleted);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(request.SearchTerm))
|
if (!string.IsNullOrWhiteSpace(request.SearchTerm))
|
||||||
{
|
{
|
||||||
tenantsQuery = tenantsQuery.Where(tenant =>
|
tenantsQuery = tenantsQuery.Where(tenant =>
|
||||||
tenant.Name.Contains(request.SearchTerm));
|
tenant.Name.Contains(request.SearchTerm));
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalCount = await tenantsQuery.CountAsync(cancellationToken);
|
var totalCount = await tenantsQuery.CountAsync(cancellationToken);
|
||||||
|
|
||||||
var tenants = await tenantsQuery
|
var tenants = await tenantsQuery
|
||||||
.Skip((request.Query.Page - 1) * request.Query.PageSize)
|
.Skip((request.Query.Page - 1) * request.Query.PageSize)
|
||||||
.Take(request.Query.PageSize)
|
.Take(request.Query.PageSize)
|
||||||
.Select(tenant => TenantViewModel.FromTenant(tenant))
|
.Select(tenant => TenantViewModel.FromTenant(tenant))
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
return new PagedResult<TenantViewModel>(
|
return new PagedResult<TenantViewModel>(
|
||||||
totalCount, tenants, request.Query.Page, request.Query.PageSize);
|
totalCount, tenants, request.Query.Page, request.Query.PageSize);
|
||||||
}
|
}
|
||||||
|
@ -29,14 +29,14 @@ public sealed class GetAllUsersQueryHandler :
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(request.SearchTerm))
|
if (!string.IsNullOrWhiteSpace(request.SearchTerm))
|
||||||
{
|
{
|
||||||
usersQuery = usersQuery.Where(user =>
|
usersQuery = usersQuery.Where(user =>
|
||||||
user.Email.Contains(request.SearchTerm) ||
|
user.Email.Contains(request.SearchTerm) ||
|
||||||
user.FirstName.Contains(request.SearchTerm) ||
|
user.FirstName.Contains(request.SearchTerm) ||
|
||||||
user.LastName.Contains(request.SearchTerm));
|
user.LastName.Contains(request.SearchTerm));
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalCount = await usersQuery.CountAsync(cancellationToken);
|
var totalCount = await usersQuery.CountAsync(cancellationToken);
|
||||||
|
|
||||||
var users = await usersQuery
|
var users = await usersQuery
|
||||||
.Skip((request.Query.Page - 1) * request.Query.PageSize)
|
.Skip((request.Query.Page - 1) * request.Query.PageSize)
|
||||||
.Take(request.Query.PageSize)
|
.Take(request.Query.PageSize)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Application.Interfaces;
|
using CleanArchitecture.Application.Interfaces;
|
||||||
using CleanArchitecture.Application.Queries.Tenants.GetAll;
|
using CleanArchitecture.Application.Queries.Tenants.GetAll;
|
||||||
@ -61,7 +60,7 @@ public sealed class TenantService : ITenantService
|
|||||||
SlidingExpiration = TimeSpan.FromDays(3),
|
SlidingExpiration = TimeSpan.FromDays(3),
|
||||||
AbsoluteExpiration = DateTimeOffset.Now.AddDays(30)
|
AbsoluteExpiration = DateTimeOffset.Now.AddDays(30)
|
||||||
});
|
});
|
||||||
|
|
||||||
return cachedTenant;
|
return cachedTenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Application.Interfaces;
|
using CleanArchitecture.Application.Interfaces;
|
||||||
using CleanArchitecture.Application.Queries.Users.GetAll;
|
using CleanArchitecture.Application.Queries.Users.GetAll;
|
||||||
|
@ -4,14 +4,15 @@ namespace CleanArchitecture.Application.ViewModels;
|
|||||||
|
|
||||||
public sealed class PageQuery
|
public sealed class PageQuery
|
||||||
{
|
{
|
||||||
|
private int _page = 1;
|
||||||
private int _pageSize = 10;
|
private int _pageSize = 10;
|
||||||
|
|
||||||
public int PageSize
|
public int PageSize
|
||||||
{
|
{
|
||||||
get => _pageSize;
|
get => _pageSize;
|
||||||
set => _pageSize = Math.Max(0, value);
|
set => _pageSize = Math.Max(0, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _page = 1;
|
|
||||||
public int Page
|
public int Page
|
||||||
{
|
{
|
||||||
get => _page;
|
get => _page;
|
||||||
|
@ -23,7 +23,6 @@ public sealed class PagedResult<T>
|
|||||||
// used by json deserializer
|
// used by json deserializer
|
||||||
private PagedResult()
|
private PagedResult()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PagedResult<T> Empty()
|
public static PagedResult<T> Empty()
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3"/>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2"/>
|
||||||
<PackageReference Include="NSubstitute" Version="5.0.0" />
|
<PackageReference Include="NSubstitute" Version="5.0.0"/>
|
||||||
<PackageReference Include="xunit" Version="2.5.0" />
|
<PackageReference Include="xunit" Version="2.5.0"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.Commands.Tenants.CreateTenant;
|
using CleanArchitecture.Domain.Commands.Tenants.CreateTenant;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.Tenant;
|
using CleanArchitecture.Shared.Events.Tenant;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Tests.CommandHandler.Tenant.CreateTenant;
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.Tenant.CreateTenant;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.Commands.Tenants.DeleteTenant;
|
using CleanArchitecture.Domain.Commands.Tenants.DeleteTenant;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.Tenant;
|
using CleanArchitecture.Shared.Events.Tenant;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Tests.CommandHandler.Tenant.DeleteTenant;
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.Tenant.DeleteTenant;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.Commands.Tenants.UpdateTenant;
|
using CleanArchitecture.Domain.Commands.Tenants.UpdateTenant;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.Tenant;
|
using CleanArchitecture.Shared.Events.Tenant;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Tests.CommandHandler.Tenant.UpdateTenant;
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.Tenant.UpdateTenant;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Commands.Users.ChangePassword;
|
using CleanArchitecture.Domain.Commands.Users.ChangePassword;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
using CleanArchitecture.Shared.Events.User;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.ChangePassword;
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.ChangePassword;
|
||||||
|
@ -2,7 +2,7 @@ using System;
|
|||||||
using CleanArchitecture.Domain.Commands.Users.CreateUser;
|
using CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
using CleanArchitecture.Shared.Events.User;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ public sealed class CreateUserCommandHandlerTests
|
|||||||
DomainErrorCodes.User.UserAlreadyExists,
|
DomainErrorCodes.User.UserAlreadyExists,
|
||||||
$"There is already a user with Id {command.UserId}");
|
$"There is already a user with Id {command.UserId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Create_Already_Existing_Email()
|
public void Should_Not_Create_Already_Existing_Email()
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
using CleanArchitecture.Shared.Events.User;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.DeleteUser;
|
namespace CleanArchitecture.Domain.Tests.CommandHandler.User.DeleteUser;
|
||||||
@ -42,7 +42,7 @@ public sealed class DeleteUserCommandHandlerTests
|
|||||||
ErrorCodes.ObjectNotFound,
|
ErrorCodes.ObjectNotFound,
|
||||||
$"There is no user with Id {command.UserId}");
|
$"There is no user with Id {command.UserId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Not_Delete_User_Insufficient_Permissions()
|
public void Should_Not_Delete_User_Insufficient_Permissions()
|
||||||
{
|
{
|
||||||
|
@ -40,7 +40,7 @@ public sealed class DeleteUserCommandTestFixture : CommandHandlerFixtureBase
|
|||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetupCurrentUser()
|
public void SetupCurrentUser()
|
||||||
{
|
{
|
||||||
User.GetUserRole().Returns(UserRole.User);
|
User.GetUserRole().Returns(UserRole.User);
|
||||||
|
@ -3,7 +3,7 @@ using System.Threading.Tasks;
|
|||||||
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
using CleanArchitecture.Shared.Events.User;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
|
using CleanArchitecture.Shared.Events;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Tests;
|
namespace CleanArchitecture.Domain.Tests;
|
||||||
|
@ -5,9 +5,13 @@ namespace CleanArchitecture.Domain;
|
|||||||
|
|
||||||
public static class CacheKeyGenerator
|
public static class CacheKeyGenerator
|
||||||
{
|
{
|
||||||
public static string GetEntityCacheKey<TEntity>(TEntity entity) where TEntity : Entity =>
|
public static string GetEntityCacheKey<TEntity>(TEntity entity) where TEntity : Entity
|
||||||
$"{typeof(TEntity)}-{entity.Id}";
|
{
|
||||||
|
return $"{typeof(TEntity)}-{entity.Id}";
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetEntityCacheKey<TEntity>(Guid id) where TEntity : Entity =>
|
public static string GetEntityCacheKey<TEntity>(Guid id) where TEntity : Entity
|
||||||
$"{typeof(TEntity)}-{id}";
|
{
|
||||||
|
return $"{typeof(TEntity)}-{id}";
|
||||||
|
}
|
||||||
}
|
}
|
@ -10,11 +10,16 @@
|
|||||||
<PackageReference Include="FluentValidation" Version="11.7.1"/>
|
<PackageReference Include="FluentValidation" Version="11.7.1"/>
|
||||||
<PackageReference Include="MediatR" Version="12.1.1"/>
|
<PackageReference Include="MediatR" Version="12.1.1"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1"/>
|
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1"/>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
|
||||||
|
<PackageReference Include="RabbitMQ.Client" Version="6.5.0"/>
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.32.1"/>
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.32.1"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\CleanArchitecture.Shared\CleanArchitecture.Shared.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -3,10 +3,10 @@ using System.Threading.Tasks;
|
|||||||
using CleanArchitecture.Domain.Entities;
|
using CleanArchitecture.Domain.Entities;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.Tenant;
|
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
|
using CleanArchitecture.Shared.Events.Tenant;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Commands.Tenants.CreateTenant;
|
namespace CleanArchitecture.Domain.Commands.Tenants.CreateTenant;
|
||||||
|
@ -3,10 +3,10 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.Tenant;
|
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
|
using CleanArchitecture.Shared.Events.Tenant;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Commands.Tenants.DeleteTenant;
|
namespace CleanArchitecture.Domain.Commands.Tenants.DeleteTenant;
|
||||||
|
@ -2,10 +2,10 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.Tenant;
|
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
|
using CleanArchitecture.Shared.Events.Tenant;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Commands.Tenants.UpdateTenant;
|
namespace CleanArchitecture.Domain.Commands.Tenants.UpdateTenant;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
|
using CleanArchitecture.Shared.Events.User;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using BC = BCrypt.Net.BCrypt;
|
using BC = BCrypt.Net.BCrypt;
|
||||||
|
|
||||||
|
@ -3,10 +3,10 @@ using System.Threading.Tasks;
|
|||||||
using CleanArchitecture.Domain.Entities;
|
using CleanArchitecture.Domain.Entities;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
|
using CleanArchitecture.Shared.Events.User;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using BC = BCrypt.Net.BCrypt;
|
using BC = BCrypt.Net.BCrypt;
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
|
using CleanArchitecture.Shared.Events.User;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
namespace CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||||
|
@ -68,7 +68,7 @@ public sealed class LoginUserCommandHandler : CommandHandlerBase,
|
|||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
user.SetActive();
|
user.SetActive();
|
||||||
user.SetLastLoggedinDate(DateTimeOffset.Now);
|
user.SetLastLoggedinDate(DateTimeOffset.Now);
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Enums;
|
using CleanArchitecture.Domain.Enums;
|
||||||
using CleanArchitecture.Domain.Errors;
|
using CleanArchitecture.Domain.Errors;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
|
using CleanArchitecture.Shared.Events.User;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
namespace CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||||
|
6
CleanArchitecture.Domain/Constants/Messaging.cs
Normal file
6
CleanArchitecture.Domain/Constants/Messaging.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace CleanArchitecture.Domain.Constants;
|
||||||
|
|
||||||
|
public sealed class Messaging
|
||||||
|
{
|
||||||
|
public const string ExchangeNameNotifications = "exchange-notifications";
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CleanArchitecture.Shared.Events;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.DomainEvents;
|
namespace CleanArchitecture.Domain.DomainEvents;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using CleanArchitecture.Shared.Events;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.DomainEvents;
|
namespace CleanArchitecture.Domain.DomainEvents;
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ public class User : Entity
|
|||||||
{
|
{
|
||||||
TenantId = tenantId;
|
TenantId = tenantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLastLoggedinDate(DateTimeOffset lastLoggedinDate)
|
public void SetLastLoggedinDate(DateTimeOffset lastLoggedinDate)
|
||||||
{
|
{
|
||||||
LastLoggedinDate = lastLoggedinDate;
|
LastLoggedinDate = lastLoggedinDate;
|
||||||
@ -76,7 +76,7 @@ public class User : Entity
|
|||||||
{
|
{
|
||||||
Status = UserStatus.Inactive;
|
Status = UserStatus.Inactive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetActive()
|
public void SetActive()
|
||||||
{
|
{
|
||||||
Status = UserStatus.Active;
|
Status = UserStatus.Active;
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using CleanArchitecture.Domain.Constants;
|
||||||
|
using CleanArchitecture.Domain.Rabbitmq;
|
||||||
|
using CleanArchitecture.Shared.Events;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.EventHandler.Fanout;
|
||||||
|
|
||||||
|
public sealed class FanoutEventHandler : IFanoutEventHandler
|
||||||
|
{
|
||||||
|
private readonly RabbitMqHandler _rabbitMqHandler;
|
||||||
|
|
||||||
|
public FanoutEventHandler(
|
||||||
|
RabbitMqHandler rabbitMqHandler)
|
||||||
|
{
|
||||||
|
_rabbitMqHandler = rabbitMqHandler;
|
||||||
|
_rabbitMqHandler.InitializeExchange(Messaging.ExchangeNameNotifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<DomainEvent> HandleDomainEventAsync(DomainEvent @event)
|
||||||
|
{
|
||||||
|
_rabbitMqHandler.EnqueueExchangeMessage(
|
||||||
|
Messaging.ExchangeNameNotifications,
|
||||||
|
@event);
|
||||||
|
|
||||||
|
return Task.FromResult(@event);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using CleanArchitecture.Shared.Events;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.EventHandler.Fanout;
|
||||||
|
|
||||||
|
public interface IFanoutEventHandler
|
||||||
|
{
|
||||||
|
Task<DomainEvent> HandleDomainEventAsync(DomainEvent @event);
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Entities;
|
using CleanArchitecture.Domain.Entities;
|
||||||
using CleanArchitecture.Domain.Events.Tenant;
|
using CleanArchitecture.Shared.Events.Tenant;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.Caching.Distributed;
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Entities;
|
using CleanArchitecture.Domain.Entities;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
using CleanArchitecture.Shared.Events.User;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.Caching.Distributed;
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
|
||||||
|
@ -7,9 +7,10 @@ using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
|||||||
using CleanArchitecture.Domain.Commands.Users.LoginUser;
|
using CleanArchitecture.Domain.Commands.Users.LoginUser;
|
||||||
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||||
using CleanArchitecture.Domain.EventHandler;
|
using CleanArchitecture.Domain.EventHandler;
|
||||||
using CleanArchitecture.Domain.Events.Tenant;
|
using CleanArchitecture.Domain.EventHandler.Fanout;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
|
using CleanArchitecture.Shared.Events.Tenant;
|
||||||
|
using CleanArchitecture.Shared.Events.User;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
@ -36,6 +37,9 @@ public static class ServiceCollectionExtension
|
|||||||
|
|
||||||
public static IServiceCollection AddNotificationHandlers(this IServiceCollection services)
|
public static IServiceCollection AddNotificationHandlers(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
// Fanout
|
||||||
|
services.AddScoped<IFanoutEventHandler, FanoutEventHandler>();
|
||||||
|
|
||||||
// User
|
// User
|
||||||
services.AddScoped<INotificationHandler<UserCreatedEvent>, UserEventHandler>();
|
services.AddScoped<INotificationHandler<UserCreatedEvent>, UserEventHandler>();
|
||||||
services.AddScoped<INotificationHandler<UserUpdatedEvent>, UserEventHandler>();
|
services.AddScoped<INotificationHandler<UserUpdatedEvent>, UserEventHandler>();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Commands;
|
using CleanArchitecture.Domain.Commands;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
using CleanArchitecture.Shared.Events;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Interfaces;
|
namespace CleanArchitecture.Domain.Interfaces;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
using CleanArchitecture.Shared.Events;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Notifications;
|
namespace CleanArchitecture.Domain.Notifications;
|
||||||
|
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class BindQueueToExchange : IRabbitMqAction
|
||||||
|
{
|
||||||
|
private readonly string _exchangeName;
|
||||||
|
private readonly string _queueName;
|
||||||
|
private readonly string _routingKey;
|
||||||
|
|
||||||
|
public BindQueueToExchange(string queueName, string exchangeName, string routingKey = "")
|
||||||
|
{
|
||||||
|
_exchangeName = exchangeName;
|
||||||
|
_routingKey = routingKey;
|
||||||
|
_queueName = queueName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
channel.QueueBind(_queueName, _exchangeName, _routingKey);
|
||||||
|
}
|
||||||
|
}
|
20
CleanArchitecture.Domain/Rabbitmq/Actions/CreateExchange.cs
Normal file
20
CleanArchitecture.Domain/Rabbitmq/Actions/CreateExchange.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class CreateExchange : IRabbitMqAction
|
||||||
|
{
|
||||||
|
private readonly string _name;
|
||||||
|
private readonly string _type;
|
||||||
|
|
||||||
|
public CreateExchange(string name, string type)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
channel.ExchangeDeclare(_name, _type);
|
||||||
|
}
|
||||||
|
}
|
23
CleanArchitecture.Domain/Rabbitmq/Actions/CreateQueue.cs
Normal file
23
CleanArchitecture.Domain/Rabbitmq/Actions/CreateQueue.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class CreateQueue : IRabbitMqAction
|
||||||
|
{
|
||||||
|
public string QueueName { get; }
|
||||||
|
|
||||||
|
public CreateQueue(string queueName)
|
||||||
|
{
|
||||||
|
QueueName = queueName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
channel.QueueDeclare(
|
||||||
|
QueueName,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public interface IRabbitMqAction
|
||||||
|
{
|
||||||
|
void Perform(IModel channel);
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class RegisterConsumer : IRabbitMqAction
|
||||||
|
{
|
||||||
|
private readonly Action<string, string, string, ConsumeEventHandler> _addConsumer;
|
||||||
|
private readonly ConsumeEventHandler _consumer;
|
||||||
|
private readonly string _exchange;
|
||||||
|
private readonly string _queue;
|
||||||
|
private readonly string _routingKey;
|
||||||
|
|
||||||
|
public RegisterConsumer(
|
||||||
|
string exchange,
|
||||||
|
string queue,
|
||||||
|
string routingKey,
|
||||||
|
ConsumeEventHandler consumer,
|
||||||
|
Action<string, string, string, ConsumeEventHandler> addConsumer)
|
||||||
|
{
|
||||||
|
_exchange = exchange;
|
||||||
|
_queue = queue;
|
||||||
|
_routingKey = routingKey;
|
||||||
|
_consumer = consumer;
|
||||||
|
_addConsumer = addConsumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
_addConsumer(_exchange, _queue, _routingKey, _consumer);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class SendAcknowledgement : IRabbitMqAction
|
||||||
|
{
|
||||||
|
public ulong DeliveryTag { get; }
|
||||||
|
|
||||||
|
public SendAcknowledgement(ulong deliveryTag)
|
||||||
|
{
|
||||||
|
DeliveryTag = deliveryTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
channel.BasicAck(DeliveryTag, false);
|
||||||
|
}
|
||||||
|
}
|
38
CleanArchitecture.Domain/Rabbitmq/Actions/SendMessage.cs
Normal file
38
CleanArchitecture.Domain/Rabbitmq/Actions/SendMessage.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
|
||||||
|
public sealed class SendMessage : IRabbitMqAction
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerSettings s_serializerSettings =
|
||||||
|
new() { TypeNameHandling = TypeNameHandling.Objects };
|
||||||
|
|
||||||
|
private readonly string _exchange;
|
||||||
|
private readonly object _message;
|
||||||
|
|
||||||
|
private readonly string _routingKey;
|
||||||
|
|
||||||
|
|
||||||
|
/// <param name="routingKey">If exchange is empty, this is the name of the queue</param>
|
||||||
|
public SendMessage(string routingKey, string exchange, object message)
|
||||||
|
{
|
||||||
|
_routingKey = routingKey;
|
||||||
|
_exchange = exchange;
|
||||||
|
_message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Perform(IModel channel)
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(_message, s_serializerSettings);
|
||||||
|
|
||||||
|
var content = Encoding.UTF8.GetBytes(json);
|
||||||
|
|
||||||
|
channel.BasicPublish(
|
||||||
|
_exchange,
|
||||||
|
_routingKey,
|
||||||
|
null,
|
||||||
|
content);
|
||||||
|
}
|
||||||
|
}
|
6
CleanArchitecture.Domain/Rabbitmq/Delegates.cs
Normal file
6
CleanArchitecture.Domain/Rabbitmq/Delegates.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq;
|
||||||
|
|
||||||
|
public delegate Task<bool> ConsumeEventHandler(ReadOnlyMemory<byte> content);
|
@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq.Extensions;
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddRabbitMqHandler(
|
||||||
|
this IServiceCollection services,
|
||||||
|
IConfiguration configuration,
|
||||||
|
string rabbitMqConfigSection)
|
||||||
|
{
|
||||||
|
var rabbitMq = new RabbitMqConfiguration();
|
||||||
|
configuration.Bind(rabbitMqConfigSection, rabbitMq);
|
||||||
|
services.AddSingleton(rabbitMq);
|
||||||
|
|
||||||
|
services.AddSingleton<RabbitMqHandler>();
|
||||||
|
services.AddHostedService(serviceProvider => serviceProvider.GetService<RabbitMqHandler>()!);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
namespace CleanArchitecture.Domain.Rabbitmq;
|
||||||
|
|
||||||
|
public sealed class RabbitMqConfiguration
|
||||||
|
{
|
||||||
|
public string Host { get; set; } = string.Empty;
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
public string Username { get; set; } = string.Empty;
|
||||||
|
public string Password { get; set; } = string.Empty;
|
||||||
|
}
|
224
CleanArchitecture.Domain/Rabbitmq/RabbitMqHandler.cs
Normal file
224
CleanArchitecture.Domain/Rabbitmq/RabbitMqHandler.cs
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CleanArchitecture.Domain.Rabbitmq.Actions;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
using RabbitMQ.Client.Events;
|
||||||
|
|
||||||
|
namespace CleanArchitecture.Domain.Rabbitmq;
|
||||||
|
|
||||||
|
public sealed class RabbitMqHandler : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly IModel? _channel;
|
||||||
|
private readonly RabbitMqConfiguration _configuration;
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, List<ConsumeEventHandler>> _consumers = new();
|
||||||
|
|
||||||
|
private readonly ILogger<RabbitMqHandler> _logger;
|
||||||
|
|
||||||
|
private readonly ConcurrentQueue<IRabbitMqAction> _pendingActions = new();
|
||||||
|
|
||||||
|
public RabbitMqHandler(
|
||||||
|
RabbitMqConfiguration configuration,
|
||||||
|
ILogger<RabbitMqHandler> logger)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
if (!configuration.Enabled)
|
||||||
|
{
|
||||||
|
logger.LogInformation("RabbitMQ is disabled. Connection will not be established");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var factory = new ConnectionFactory
|
||||||
|
{
|
||||||
|
AutomaticRecoveryEnabled = true,
|
||||||
|
HostName = configuration.Host,
|
||||||
|
UserName = configuration.Username,
|
||||||
|
Password = configuration.Password,
|
||||||
|
DispatchConsumersAsync = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var connection = factory.CreateConnection();
|
||||||
|
_channel = connection.CreateModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeExchange(string exchangeName, string type = ExchangeType.Fanout)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"RabbitMQ is disabled. Skipping the creation of exchange {exchangeName}.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(new CreateExchange(exchangeName, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeQueues(params string[] queueNames)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping the creation of queues.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var queue in queueNames)
|
||||||
|
{
|
||||||
|
_pendingActions.Enqueue(new CreateQueue(queue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BindQueueToExchange(string queueName, string exchangeName, string routingKey = "")
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping the binding of queue to exchange.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(new BindQueueToExchange(queueName, exchangeName, routingKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddConsumer(string queueName, ConsumeEventHandler consumer)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping the addition of consumer.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// routingKey is set to queueName to mimic rabbitMQ
|
||||||
|
_pendingActions.Enqueue(
|
||||||
|
new RegisterConsumer(
|
||||||
|
string.Empty,
|
||||||
|
queueName,
|
||||||
|
queueName,
|
||||||
|
consumer,
|
||||||
|
AddEventConsumer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddExchangeConsumer(string exchange, string routingKey, string queue, ConsumeEventHandler consumer)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping the addition of exchange consumer.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(
|
||||||
|
new RegisterConsumer(
|
||||||
|
exchange,
|
||||||
|
queue,
|
||||||
|
routingKey,
|
||||||
|
consumer,
|
||||||
|
AddEventConsumer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddExchangeConsumer(string exchange, string queue, ConsumeEventHandler consumer)
|
||||||
|
{
|
||||||
|
AddExchangeConsumer(exchange, string.Empty, queue, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddEventConsumer(string exchange, string queueName, string routingKey, ConsumeEventHandler consumer)
|
||||||
|
{
|
||||||
|
var key = $"{exchange}-{routingKey}";
|
||||||
|
|
||||||
|
if (!_consumers.TryGetValue(key, out var consumers))
|
||||||
|
{
|
||||||
|
consumers = new List<ConsumeEventHandler>();
|
||||||
|
_consumers.TryAdd(key, consumers);
|
||||||
|
|
||||||
|
var eventHandler = new AsyncEventingBasicConsumer(_channel);
|
||||||
|
eventHandler.Received += CallEventConsumersAsync;
|
||||||
|
|
||||||
|
_channel!.BasicConsume(queueName, false, eventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
consumers.Add(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CallEventConsumersAsync(object sender, BasicDeliverEventArgs ea)
|
||||||
|
{
|
||||||
|
var key = $"{ea.Exchange}-{ea.RoutingKey}";
|
||||||
|
|
||||||
|
if (!_consumers.TryGetValue(key, out var consumers))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var consumer in consumers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await consumer(ea.Body);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, $"Error while handling event in queue {ea.RoutingKey}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(new SendAcknowledgement(ea.DeliveryTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void EnqueueMessage(string queueName, object message)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping enqueueing of message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(new SendMessage(queueName, string.Empty, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnqueueExchangeMessage(string exchange, object message, string routingKey = "")
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Skipping enqueueing of message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingActions.Enqueue(new SendMessage(routingKey, exchange, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
if (!_configuration.Enabled)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("RabbitMQ is disabled. Message handling loop will not be started");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
HandleEnqueuedActions();
|
||||||
|
|
||||||
|
await Task.Delay(1000, stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleEnqueuedActions()
|
||||||
|
{
|
||||||
|
while (_pendingActions.TryDequeue(out var action))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action.Perform(_channel!);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error while trying to send a rabbitmq message");
|
||||||
|
_pendingActions.Enqueue(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,10 +8,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2"/>
|
||||||
<PackageReference Include="NSubstitute" Version="5.0.0" />
|
<PackageReference Include="NSubstitute" Version="5.0.0"/>
|
||||||
<PackageReference Include="xunit" Version="2.5.0" />
|
<PackageReference Include="xunit" Version="2.5.0"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -23,7 +23,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -2,8 +2,9 @@ using System;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
using CleanArchitecture.Domain.DomainEvents;
|
||||||
using CleanArchitecture.Domain.Events.User;
|
using CleanArchitecture.Domain.EventHandler.Fanout;
|
||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
|
using CleanArchitecture.Shared.Events.User;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -17,8 +18,9 @@ public sealed class InMemoryBusTests
|
|||||||
{
|
{
|
||||||
var mediator = Substitute.For<IMediator>();
|
var mediator = Substitute.For<IMediator>();
|
||||||
var domainEventStore = Substitute.For<IDomainEventStore>();
|
var domainEventStore = Substitute.For<IDomainEventStore>();
|
||||||
|
var fanoutEventHandler = Substitute.For<IFanoutEventHandler>();
|
||||||
|
|
||||||
var inMemoryBus = new InMemoryBus(mediator, domainEventStore);
|
var inMemoryBus = new InMemoryBus(mediator, domainEventStore, fanoutEventHandler);
|
||||||
|
|
||||||
const string key = "Key";
|
const string key = "Key";
|
||||||
const string value = "Value";
|
const string value = "Value";
|
||||||
@ -36,8 +38,9 @@ public sealed class InMemoryBusTests
|
|||||||
{
|
{
|
||||||
var mediator = Substitute.For<IMediator>();
|
var mediator = Substitute.For<IMediator>();
|
||||||
var domainEventStore = Substitute.For<IDomainEventStore>();
|
var domainEventStore = Substitute.For<IDomainEventStore>();
|
||||||
|
var fanoutEventHandler = Substitute.For<IFanoutEventHandler>();
|
||||||
|
|
||||||
var inMemoryBus = new InMemoryBus(mediator, domainEventStore);
|
var inMemoryBus = new InMemoryBus(mediator, domainEventStore, fanoutEventHandler);
|
||||||
|
|
||||||
var userDeletedEvent = new UserDeletedEvent(Guid.NewGuid(), Guid.NewGuid());
|
var userDeletedEvent = new UserDeletedEvent(Guid.NewGuid(), Guid.NewGuid());
|
||||||
|
|
||||||
@ -51,8 +54,9 @@ public sealed class InMemoryBusTests
|
|||||||
{
|
{
|
||||||
var mediator = Substitute.For<IMediator>();
|
var mediator = Substitute.For<IMediator>();
|
||||||
var domainEventStore = Substitute.For<IDomainEventStore>();
|
var domainEventStore = Substitute.For<IDomainEventStore>();
|
||||||
|
var fanoutEventHandler = Substitute.For<IFanoutEventHandler>();
|
||||||
|
|
||||||
var inMemoryBus = new InMemoryBus(mediator, domainEventStore);
|
var inMemoryBus = new InMemoryBus(mediator, domainEventStore, fanoutEventHandler);
|
||||||
|
|
||||||
var deleteUserCommand = new DeleteUserCommand(Guid.NewGuid());
|
var deleteUserCommand = new DeleteUserCommand(Guid.NewGuid());
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
|
<ProjectReference Include="..\CleanArchitecture.Shared\CleanArchitecture.Shared.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -3,6 +3,7 @@ using CleanArchitecture.Domain.DomainEvents;
|
|||||||
using CleanArchitecture.Domain.DomainNotifications;
|
using CleanArchitecture.Domain.DomainNotifications;
|
||||||
using CleanArchitecture.Domain.Notifications;
|
using CleanArchitecture.Domain.Notifications;
|
||||||
using CleanArchitecture.Infrastructure.Database;
|
using CleanArchitecture.Infrastructure.Database;
|
||||||
|
using CleanArchitecture.Shared.Events;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace CleanArchitecture.Infrastructure.EventSourcing;
|
namespace CleanArchitecture.Infrastructure.EventSourcing;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CleanArchitecture.Domain.Commands;
|
using CleanArchitecture.Domain.Commands;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
using CleanArchitecture.Domain.DomainEvents;
|
||||||
|
using CleanArchitecture.Domain.EventHandler.Fanout;
|
||||||
using CleanArchitecture.Domain.Interfaces;
|
using CleanArchitecture.Domain.Interfaces;
|
||||||
|
using CleanArchitecture.Shared.Events;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace CleanArchitecture.Infrastructure;
|
namespace CleanArchitecture.Infrastructure;
|
||||||
@ -9,14 +11,17 @@ namespace CleanArchitecture.Infrastructure;
|
|||||||
public sealed class InMemoryBus : IMediatorHandler
|
public sealed class InMemoryBus : IMediatorHandler
|
||||||
{
|
{
|
||||||
private readonly IDomainEventStore _domainEventStore;
|
private readonly IDomainEventStore _domainEventStore;
|
||||||
|
private readonly IFanoutEventHandler _fanoutEventHandler;
|
||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
|
|
||||||
public InMemoryBus(
|
public InMemoryBus(
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
IDomainEventStore domainEventStore)
|
IDomainEventStore domainEventStore,
|
||||||
|
IFanoutEventHandler fanoutEventHandler)
|
||||||
{
|
{
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
_domainEventStore = domainEventStore;
|
_domainEventStore = domainEventStore;
|
||||||
|
_fanoutEventHandler = fanoutEventHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TResponse> QueryAsync<TResponse>(IRequest<TResponse> query)
|
public Task<TResponse> QueryAsync<TResponse>(IRequest<TResponse> query)
|
||||||
@ -29,6 +34,8 @@ public sealed class InMemoryBus : IMediatorHandler
|
|||||||
await _domainEventStore.SaveAsync(@event);
|
await _domainEventStore.SaveAsync(@event);
|
||||||
|
|
||||||
await _mediator.Publish(@event);
|
await _mediator.Publish(@event);
|
||||||
|
|
||||||
|
await _fanoutEventHandler.HandleDomainEventAsync(@event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SendCommandAsync<T>(T command) where T : CommandBase
|
public Task SendCommandAsync<T>(T command) where T : CommandBase
|
||||||
|
@ -8,14 +8,14 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.10" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="7.0.10" />
|
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2"/>
|
||||||
<PackageReference Include="xunit" Version="2.5.0" />
|
<PackageReference Include="xunit" Version="2.5.0"/>
|
||||||
<PackageReference Include="Xunit.Priority" Version="1.1.6" />
|
<PackageReference Include="Xunit.Priority" Version="1.1.6"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -27,8 +27,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Api\CleanArchitecture.Api.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Api\CleanArchitecture.Api.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,16 +6,16 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Protobuf Include="Users\Models.proto" GrpcServices="Both" />
|
<Protobuf Include="Users\Models.proto" GrpcServices="Both"/>
|
||||||
<Protobuf Include="Users\UsersApi.proto" GrpcServices="Both" />
|
<Protobuf Include="Users\UsersApi.proto" GrpcServices="Both"/>
|
||||||
<Protobuf Include="Tenants\Models.proto" GrpcServices="Both" />
|
<Protobuf Include="Tenants\Models.proto" GrpcServices="Both"/>
|
||||||
<Protobuf Include="Tenants\TenantsApi.proto" GrpcServices="Both" />
|
<Protobuf Include="Tenants\TenantsApi.proto" GrpcServices="Both"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.24.2" />
|
<PackageReference Include="Google.Protobuf" Version="3.24.2"/>
|
||||||
<PackageReference Include="Google.Protobuf.Tools" Version="3.24.2" />
|
<PackageReference Include="Google.Protobuf.Tools" Version="3.24.2"/>
|
||||||
<PackageReference Include="Grpc.AspNetCore" Version="2.56.0" />
|
<PackageReference Include="Grpc.AspNetCore" Version="2.56.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -6,4 +6,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MediatR" Version="12.1.1"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.DomainEvents;
|
namespace CleanArchitecture.Shared.Events;
|
||||||
|
|
||||||
public abstract class DomainEvent : Message, INotification
|
public abstract class DomainEvent : Message, INotification
|
||||||
{
|
{
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.DomainEvents;
|
namespace CleanArchitecture.Shared.Events;
|
||||||
|
|
||||||
public abstract class Message : IRequest
|
public abstract class Message : IRequest
|
||||||
{
|
{
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Events.Tenant;
|
namespace CleanArchitecture.Shared.Events.Tenant;
|
||||||
|
|
||||||
public sealed class TenantCreatedEvent : DomainEvent
|
public sealed class TenantCreatedEvent : DomainEvent
|
||||||
{
|
{
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Events.Tenant;
|
namespace CleanArchitecture.Shared.Events.Tenant;
|
||||||
|
|
||||||
public sealed class TenantDeletedEvent : DomainEvent
|
public sealed class TenantDeletedEvent : DomainEvent
|
||||||
{
|
{
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Events.Tenant;
|
namespace CleanArchitecture.Shared.Events.Tenant;
|
||||||
|
|
||||||
public sealed class TenantUpdatedEvent : DomainEvent
|
public sealed class TenantUpdatedEvent : DomainEvent
|
||||||
{
|
{
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Events.User;
|
namespace CleanArchitecture.Shared.Events.User;
|
||||||
|
|
||||||
public sealed class PasswordChangedEvent : DomainEvent
|
public sealed class PasswordChangedEvent : DomainEvent
|
||||||
{
|
{
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Events.User;
|
namespace CleanArchitecture.Shared.Events.User;
|
||||||
|
|
||||||
public sealed class UserCreatedEvent : DomainEvent
|
public sealed class UserCreatedEvent : DomainEvent
|
||||||
{
|
{
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Events.User;
|
namespace CleanArchitecture.Shared.Events.User;
|
||||||
|
|
||||||
public sealed class UserDeletedEvent : DomainEvent
|
public sealed class UserDeletedEvent : DomainEvent
|
||||||
{
|
{
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CleanArchitecture.Domain.DomainEvents;
|
|
||||||
|
|
||||||
namespace CleanArchitecture.Domain.Events.User;
|
namespace CleanArchitecture.Shared.Events.User;
|
||||||
|
|
||||||
public sealed class UserUpdatedEvent : DomainEvent
|
public sealed class UserUpdatedEvent : DomainEvent
|
||||||
{
|
{
|
@ -8,11 +8,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2"/>
|
||||||
<PackageReference Include="MockQueryable.NSubstitute" Version="7.0.0" />
|
<PackageReference Include="MockQueryable.NSubstitute" Version="7.0.0"/>
|
||||||
<PackageReference Include="NSubstitute" Version="5.0.0" />
|
<PackageReference Include="NSubstitute" Version="5.0.0"/>
|
||||||
<PackageReference Include="xunit" Version="2.5.0" />
|
<PackageReference Include="xunit" Version="2.5.0"/>
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -24,9 +24,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj"/>
|
||||||
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj" />
|
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -49,6 +49,7 @@ options.ConfigurationOptions = new ConfigurationOptions
|
|||||||
EndPoints = { "localhost", "6379" }
|
EndPoints = { "localhost", "6379" }
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
3. RabbitMq: `docker run --name rabbitmq -d -p 5672:5672 -p 15672:15672 rabbitmq:3-management`
|
||||||
|
|
||||||
Running the container
|
Running the container
|
||||||
1. Build the Dockerfile: `docker build -t clean-architecture .`
|
1. Build the Dockerfile: `docker build -t clean-architecture .`
|
||||||
@ -64,7 +65,8 @@ Running the container
|
|||||||
|
|
||||||
1. Change the ConnectionString in the appsettings.json to `Server=clean-architecture-db-service;Database=clean-architecture;Trusted_Connection=False;MultipleActiveResultSets=true;TrustServerCertificate=True;User Id=SA;Password=Password123!#`
|
1. Change the ConnectionString in the appsettings.json to `Server=clean-architecture-db-service;Database=clean-architecture;Trusted_Connection=False;MultipleActiveResultSets=true;TrustServerCertificate=True;User Id=SA;Password=Password123!#`
|
||||||
2. Change the RedisHostName in the appsettings.json to `redis-service`
|
2. Change the RedisHostName in the appsettings.json to `redis-service`
|
||||||
3. Build the docker image and push it to the docker hub (Change the image name in the `k8s-deployment.yml` to your own)
|
3. Change the RabbitMQ Host in the appsettings.json to `rabbitmq-service`
|
||||||
|
4. Build the docker image and push it to the docker hub (Change the image name in the `k8s-deployment.yml` to your own)
|
||||||
Apply the deployment file: `kubectl apply -f k8s-deployment.yml` (Delete: `kubectl delete -f k8s-deployment.yml`)
|
Apply the deployment file: `kubectl apply -f k8s-deployment.yml` (Delete: `kubectl delete -f k8s-deployment.yml`)
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,10 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 80:80
|
- 80:80
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
db:
|
||||||
|
condition: service_started
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
links:
|
links:
|
||||||
- db
|
- db
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@ -15,6 +18,7 @@ services:
|
|||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: mcr.microsoft.com/mssql/server
|
image: mcr.microsoft.com/mssql/server
|
||||||
environment:
|
environment:
|
||||||
@ -22,6 +26,7 @@ services:
|
|||||||
- SA_PASSWORD=Password123!#
|
- SA_PASSWORD=Password123!#
|
||||||
ports:
|
ports:
|
||||||
- 1433:1433
|
- 1433:1433
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: docker.io/bitnami/redis:7.2
|
image: docker.io/bitnami/redis:7.2
|
||||||
environment:
|
environment:
|
||||||
@ -32,6 +37,24 @@ services:
|
|||||||
- '6379:6379'
|
- '6379:6379'
|
||||||
volumes:
|
volumes:
|
||||||
- 'redis_data:/bitnami/redis/data'
|
- 'redis_data:/bitnami/redis/data'
|
||||||
|
|
||||||
|
rabbitmq:
|
||||||
|
image: "rabbitmq:3-management"
|
||||||
|
ports:
|
||||||
|
- 5672:5672
|
||||||
|
- 15672:15672
|
||||||
|
environment:
|
||||||
|
- RABBITMQ_DEFAULT_USER=admin
|
||||||
|
- RABBITMQ_DEFAULT_PASS=DOIA9234JF
|
||||||
|
volumes:
|
||||||
|
- rabbitmq_data:/var/lib/rabbitmq
|
||||||
|
healthcheck:
|
||||||
|
test: rabbitmq-diagnostics -q ping
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
rabbitmq_data:
|
||||||
redis_data:
|
redis_data:
|
||||||
driver: local
|
driver: local
|
||||||
|
@ -14,7 +14,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: clean-architecture-app
|
- name: clean-architecture-app
|
||||||
# Replace this with the path to your built image
|
# Replace this with the path to your built image
|
||||||
image: alexdev28/clean-architecture
|
image: alexdev28/clean-architecture
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
@ -118,3 +118,54 @@ spec:
|
|||||||
- protocol: TCP
|
- protocol: TCP
|
||||||
port: 6379
|
port: 6379
|
||||||
targetPort: 6379
|
targetPort: 6379
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: rabbitmq-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: rabbitmq
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: rabbitmq
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: rabbitmq
|
||||||
|
image: rabbitmq:3-management
|
||||||
|
ports:
|
||||||
|
- containerPort: 5672
|
||||||
|
- containerPort: 15672
|
||||||
|
env:
|
||||||
|
- name: RABBITMQ_DEFAULT_USER
|
||||||
|
value: admin
|
||||||
|
- name: RABBITMQ_DEFAULT_PASS
|
||||||
|
value: DOIA9234JF
|
||||||
|
volumeMounts:
|
||||||
|
- name: rabbitmq-data
|
||||||
|
mountPath: /var/lib/rabbitmq
|
||||||
|
volumes:
|
||||||
|
- name: rabbitmq-data
|
||||||
|
emptyDir: {}
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: rabbitmq-service
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: rabbitmq
|
||||||
|
ports:
|
||||||
|
- name: rabbitmq-port
|
||||||
|
protocol: TCP
|
||||||
|
port: 5672
|
||||||
|
targetPort: 5672
|
||||||
|
- name: rabbitmq-management-port
|
||||||
|
protocol: TCP
|
||||||
|
port: 15672
|
||||||
|
targetPort: 15672
|
||||||
|
Loading…
Reference in New Issue
Block a user