Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Riber - Changelog

## v4.1.1 - 02/11/2025

- **NOVO**: Sistema de mensageria com MassTransit e RabbitMQ
- Adiciona `IMessagePublisher` para publicação de mensagens assíncronas
- Implementa `MassTransitMessagePublisher` para integração com MassTransit
- Adiciona `MassTransitPublishContextWrapper` para configuração de contexto de publicação
- Implementa `SendEmailMessageConsumer` para processamento assíncrono de e-mails
- Adiciona mensagem `SendEmailMessage` para comunicação entre serviços
- **REFATORAÇÃO**: Migração do sistema de envio de e-mails para arquitetura baseada em mensageria
- Remove implementação síncrona antiga de envio de e-mails
- Atualiza handlers para usar publicação de mensagens em vez de chamadas diretas
- Adiciona configuração do RabbitMQ no Docker Compose
- **MELHORIA**: Melhor separação de responsabilidades e desacoplamento entre camadas
- Sistema de e-mails agora opera de forma assíncrona e resiliente

---

## v4.1.0 - 26/10/2025

- **NOVO**: Sistema modular de serviços de autenticação com separação de responsabilidades
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Companies>Samuel Zedec</Companies>
<Copyright>Copyright © $([System.DateTime]::Now.Year)</Copyright>
<Description>Sistema de gestão financeira para um lanchonete local</Description>
<Version>4.1.0</Version>
<Version>4.1.1</Version>
</PropertyGroup>

<!-- Repositório e Documentação -->
Expand Down
106 changes: 62 additions & 44 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,82 +3,100 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<!-- Pacotes que contém somente abstrações -->
<!-- Core Framework & Runtime -->
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="9.10.1" />
<PackageVersion Include="Microsoft.Extensions.AI" Version="9.10.1" />
</ItemGroup>
<!-- Web & API -->
<ItemGroup>
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.10" />
<PackageVersion Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageVersion Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageVersion Include="Mediator.Abstractions" Version="3.0.1" />
<PackageVersion Include="Scalar.AspNetCore" Version="2.9.0" />
</ItemGroup>
<!-- Pacotes específicos da camada Application -->
<!-- Database & Entity Framework -->
<ItemGroup>
<PackageVersion Include="FluentValidation" Version="12.0.0" />
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="12.0.0" />
<PackageVersion Include="Mediator.SourceGenerator" Version="3.0.1">
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageVersion>
<PackageVersion Include="Microsoft.SemanticKernel.Abstractions" Version="1.66.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="9.9.1" />
</ItemGroup>
<!-- Pacotes específicos da camada Infrastructure -->
<ItemGroup>
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.10" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageVersion Include="Pgvector.EntityFrameworkCore" Version="0.2.2" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10">
</ItemGroup>
<!-- Mediator & CQRS -->
<ItemGroup>
<PackageVersion Include="Mediator.Abstractions" Version="3.0.1" />
<PackageVersion Include="Mediator.SourceGenerator" Version="3.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageVersion>
</ItemGroup>
<!-- Validation -->
<ItemGroup>
<PackageVersion Include="FluentValidation" Version="12.0.0" />
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="12.0.0" />
</ItemGroup>
<!-- AI & LLM -->
<ItemGroup>
<PackageVersion Include="Anthropic.SDK" Version="5.8.0" />
<PackageVersion Include="OllamaSharp" Version="5.4.8" />
</ItemGroup>
<!-- Background Jobs & Scheduling -->
<ItemGroup>
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.15.1" />
<PackageVersion Include="Quartz.Extensions.Hosting" Version="3.15.1" />
<PackageVersion Include="Quartz.Serialization.Json" Version="3.15.1" />
</ItemGroup>
<!-- Messaging & Service Bus -->
<ItemGroup>
<PackageVersion Include="MassTransit.Abstractions" Version="8.5.5" />
<PackageVersion Include="MassTransit.RabbitMQ" Version="8.5.5" />
</ItemGroup>
<!-- Health Checks -->
<ItemGroup>
<PackageVersion Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
<PackageVersion Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.15.0" />
<PackageVersion Include="Quartz.Extensions.Hosting" Version="3.15.0" />
<PackageVersion Include="Quartz.Serialization.Json" Version="3.15.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.66.0-alpha" />
<PackageVersion Include="Microsoft.SemanticKernel.Core" Version="1.66.0" />
<PackageVersion Include="Pgvector.EntityFrameworkCore" Version="0.2.2" />
<PackageVersion Include="Microsoft.Extensions.AI" Version="9.9.1" />
</ItemGroup>
<!-- Pacotes Pacotes específicos da camada Api -->
<!-- Logging & Observability -->
<ItemGroup>
<PackageVersion Include="Scalar.AspNetCore" Version="2.8.11" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="9.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.10" />
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.13.1" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.13.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.13.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.13.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.13.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Quartz" Version="1.12.0-beta.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.13.0" />
</ItemGroup>
<!-- Pacotes para testes -->
<!-- AWS Services -->
<ItemGroup>
<PackageVersion Include="AWSSDK.Extensions.NETCore.Setup" Version="4.0.3.9" />
<PackageVersion Include="AWSSDK.SimpleEmail" Version="4.0.2" />
<PackageVersion Include="AWSSDK.S3" Version="4.0.9.1" />
</ItemGroup>
<!-- Testing -->
<ItemGroup>
<PackageVersion Include="Testcontainers" Version="4.7.0" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.7.0" />
<PackageVersion Include="FluentAssertions" Version="8.7.1" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="Bogus" Version="35.6.4" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.10" />
<PackageVersion Include="FluentAssertions" Version="8.8.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="Moq.EntityFrameworkCore" Version="9.0.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.10" />
<PackageVersion Include="MockQueryable.Moq" Version="9.0.0" />
<PackageVersion Include="Bogus" Version="35.6.5" />
<PackageVersion Include="Testcontainers" Version="4.8.1" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.8.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.10" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageVersion>
</ItemGroup>
<!-- Pacotes AWS -->
<ItemGroup>
<PackageVersion Include="AWSSDK.Extensions.NETCore.Setup" Version="4.0.3.6" />
<PackageVersion Include="AWSSDK.SimpleEmail" Version="4.0.1.8" />
<PackageVersion Include="AWSSDK.S3" Version="4.0.7.9" />
</ItemGroup>
</Project>
15 changes: 15 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ services:
ASPNETCORE_ENVIRONMENT: Development
ConnectionStrings__DefaultConnection: Host=postgres;Database=riber_db;Username=postgres;Password=root
OTEL_EXPORTER_OTLP_ENDPOINT: http://dashboard:18889
RabbitMQ__Host: "amqp://rabbitmq:5672"
RabbitMQ__Username: "admin"
RabbitMQ__Password: "admin123"

sonarqube:
profiles: [ "analysis" ]
Expand All @@ -54,6 +57,18 @@ services:
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs

rabbitmq:
image: rabbitmq:4.2.0-management-alpine
container_name: riber-rabbitmq-dev
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_DEFAULT_USER: admin
RABBITMQ_DEFAULT_PASS: admin123
RABBITMQ_DEFAULT_VHOST: /
restart: unless-stopped

dashboard:
image: mcr.microsoft.com/dotnet/aspire-dashboard:latest
container_name: riber-aspire-dashboard
Expand Down
19 changes: 12 additions & 7 deletions src/Riber.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=riber_db;Username=postgres;Password=root;Port=5432"
},
"RabbitMQ": {
"Host": "amqp://localhost:5672",
"UserName": "guest",
"Password": "guest"
},
"AWS": {
"Region": "us-east-1",
"SES": {
Expand All @@ -23,12 +28,12 @@
"Audience": "Rb.RefreshToken.Client",
"ExpirationInDays": 7
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:18889",
"AllowedHosts": "*"
"Anthropic": {
"ApiKey": ""
},
"Ollama": {
"ApiUri": "http://localhost:11434",
"EmbeddingModel": "mxbai-embed-large:latest"
}
}
18 changes: 0 additions & 18 deletions src/Riber.Application/Abstractions/Dispatchers/IEmailDispatcher.cs

This file was deleted.

33 changes: 33 additions & 0 deletions src/Riber.Application/Abstractions/Messaging/IMessagePublisher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Riber.Application.Abstractions.Messaging;

/// <summary>
/// Define o contrato para publicação de mensagens de forma assíncrona em um sistema de mensageria.
/// </summary>
public interface IMessagePublisher
{
/// <summary>
/// Publica uma mensagem de forma assíncrona para um sistema de mensageria.
/// </summary>
/// <typeparam name="TMessage">O tipo da mensagem a ser publicada.</typeparam>
/// <param name="message">A instância da mensagem a ser publicada.</param>
/// <param name="cancellationToken">Um token que pode ser usado para solicitar o cancelamento da operação.</param>
/// <returns>Uma task que representa a operação assíncrona de publicação.</returns>
Task PublishAsync<TMessage>(
TMessage message,
CancellationToken cancellationToken = default)
where TMessage : class;

/// <summary>
/// Publica uma mensagem de forma assíncrona para um sistema de mensageria com opções configuráveis.
/// </summary>
/// <typeparam name="TMessage">O tipo da mensagem a ser publicada.</typeparam>
/// <param name="message">A instância da mensagem a ser publicada.</param>
/// <param name="configure">Uma ação que permite configurar o contexto de publicação, como prioridade e cabeçalhos.</param>
/// <param name="cancellationToken">Um token que pode ser usado para solicitar o cancelamento da operação.</param>
/// <returns>Uma task que representa a operação assíncrona de publicação.</returns>
Task PublishAsync<TMessage>(
TMessage message,
Action<IPublishContext>? configure,
CancellationToken cancellationToken = default)
where TMessage : class;
}
26 changes: 26 additions & 0 deletions src/Riber.Application/Abstractions/Messaging/IPublishContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace Riber.Application.Abstractions.Messaging;

/// <summary>
/// Representa um contexto para configurar parâmetros de publicação de mensagens como prioridade, atraso e cabeçalhos.
/// </summary>
public interface IPublishContext
{
/// <summary>
/// Define o nível de prioridade da mensagem.
/// </summary>
/// <param name="priority">O nível de prioridade a ser atribuído à mensagem.</param>
void SetPriority(byte priority);

/// <summary>
/// Define um tempo de atraso para o processamento da mensagem.
/// </summary>
/// <param name="delay">A duração do atraso antes que a mensagem seja processada.</param>
void SetDelay(TimeSpan delay);

/// <summary>
/// Define um cabeçalho com a chave e valor especificados.
/// </summary>
/// <param name="key">A chave do cabeçalho a ser definido.</param>
/// <param name="value">O valor associado à chave especificada.</param>
void SetHeader(string key, object value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ public interface IEmailService
/// </summary>
/// <param name="to">O endereço de e-mail do destinatário.</param>
/// <param name="subject">O assunto do e-mail.</param>
/// <param name="body">O conteúdo do corpo do e-mail.</param>
/// <param name="emailAddress">O endereço de e-mail do remetente como uma string.</param>
/// <param name="htmlContent">O conteúdo do corpo do e-mail.</param>
/// <param name="from">O endereço de e-mail do remetente como uma string.</param>
/// <returns>Uma tarefa que representa a operação assíncrona de envio do e-mail.</returns>
Task SendAsync(string to, string subject, string body, string emailAddress);
Task SendAsync(string to, string subject, string htmlContent, string from);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using Newtonsoft.Json.Linq;

namespace Riber.Application.Abstractions.Services.Email;

/// <summary>
Expand All @@ -8,9 +6,10 @@ namespace Riber.Application.Abstractions.Services.Email;
public interface IEmailTemplateRender
{
/// <summary>
/// Renderiza um template de email substituindo as variáveis pelos dados fornecidos.
/// Gera um template de e-mail processado, renderizando dados em um template especificado.
/// </summary>
/// <param name="data">Dados dinâmicos para substituição no template.</param>
/// <returns>Template de email renderizado como string.</returns>
Task<string> GetTemplateAsync(JObject data);
/// <param name="templatePath">O caminho do arquivo ou identificador do template a ser processado.</param>
/// <param name="data">Um dicionário contendo pares chave-valor para os dados a serem renderizados no template.</param>
/// <returns>Uma task representando a operação assíncrona. O resultado da task contém o template de e-mail renderizado como string.</returns>
Task<string> GetTemplateAsync(string templatePath, Dictionary<string, object?> data);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Riber.Application.Dtos.Email;

public sealed record WelcomeBaseEmailDto(
public sealed record WelcomeEmailDto(
string Name,
string To,
string Subject,
Expand Down
Loading
Loading