Mensageria corporativa de alto desempenho: Outbox transacional, Inbox idempotente, sanitização de PII (LGPD), contratos versionados e suporte agnóstico a RabbitMQ, Azure Service Bus e AWS SQS/SNS.
- ✨ Por que usar
- 📦 Pacotes
- 🧬 Ecossistema TEC
- 📥 Instalação
- 🚀 Início rápido
- 🧩 Diagrama de arquitetura
- 📚 Documentação
- ⚡ Compatibilidade
- 🛡️ Segurança
- 🧪 Testes
- 🤝 Contribuição
- 🏷️ Versionamento
- 📄 Licença
| Sem o TEC.Messaging | Com o TEC.Messaging |
|---|---|
| Publica no broker dentro do caso de uso: commit falha, evento já saiu (ou o contrário) | Outbox transacional: o evento é gravado na mesma transação do agregado e publicado depois |
| Um relay trava a fila inteira numa mensagem com defeito | Falha isolada: backoff exponencial por mensagem e status Dead depois de N tentativas |
| Publicação dentro de transação longa, com travas no banco | Reserva (lease) em transação curta e publicação fora da transação |
| Evento de domínio serializado direto para outros serviços | Contratos de integração com nome e versão estáveis ([IntegrationEvent]) e mapeadores |
| Reentrega processa duas vezes | Inbox: registro da mensagem processada na mesma transação do efeito |
Retry com Thread.Sleep e mensagens perdidas |
RabbitMQ com publisher confirms, filas de espera, DLQ, pausa e administração da DLQ |
| Trace e correlação quebrados entre serviços | Correlação, causa e traceparent propagados; métricas e spans TEC.Messaging |
| Pacote | Para que serve | Quando instalar | Depende de |
|---|---|---|---|
TEC.Messaging |
Envelope, contratos, mapeadores, Outbox/Inbox (abstrações), sanitizador LGPD de PII, relay, correlação, métricas | Sempre | TEC.Core |
TEC.Messaging.SqlServer |
Outbox e Inbox no SQL Server com EF Core, interceptor de eventos de domínio, administração | Persistência em SQL Server com EF Core | TEC.Messaging, EF Core |
TEC.Messaging.PostgreSql |
Outbox e Inbox no PostgreSQL com EF Core (FOR UPDATE SKIP LOCKED, RETURNING), interceptor |
Persistência em PostgreSQL com EF Core | TEC.Messaging, EF Core, Npgsql |
TEC.Messaging.RabbitMQ |
Publicador com confirms, consumidores com concorrência paralela, retry escalonado, DLQ, pausa, health check | Broker RabbitMQ 3.13+/4.x | TEC.Messaging, TEC.Vault, RabbitMQ.Client 7, Polly.Core |
TEC.Messaging.AzureServiceBus |
Publicador e consumidor para filas e tópicos do Azure Service Bus, confirms, reenvio agendado com espera crescente, DLQ, concorrência, rotação de credencial, health check | Nuvem Microsoft Azure | TEC.Messaging, TEC.Vault, Azure.Messaging.ServiceBus |
TEC.Messaging.Aws |
Publicador e consumidor para filas Amazon SQS e tópicos SNS, long polling com renovação de visibilidade, concorrência, DLQ, rotação de credencial, health check | Nuvem Amazon Web Services (AWS) | TEC.Messaging, TEC.Vault, AWSSDK (SQS/SNS) |
Os seis pacotes têm a mesma versão e saem juntos.
flowchart BT
Core["🧰 TEC.Core<br/><sub>Result, erros, domínio</sub>"]
Vault["🔐 TEC.Vault<br/><sub>segredos, chaves, certificados</sub>"]
Cqrs["🧭 TEC.Cqrs<br/><sub>commands, queries, pipeline</sub>"]
Sec["🛡️ TEC.Security<br/><sub>autenticação, autorização</sub>"]
Obs["📡 TEC.Observability<br/><sub>traces, métricas, logs</sub>"]
Orm["🗄️ TEC.ORM<br/><sub>persistência</sub>"]
Msg["📮 TEC.Messaging<br/><sub>Outbox, Inbox, brokers</sub>"]
Vault --> Core
Cqrs --> Core
Sec --> Core
Sec --> Vault
Orm --> Core
Orm --> Cqrs
Orm --> Vault
Msg --> Core
Msg --> Vault
style Msg fill:#1f6feb,color:#fff
O TEC.Messaging não depende do TEC.Cqrs nem do TEC.ORM: funciona de forma autônoma com qualquer DbContext. Traces, logs e métricas TEC.Messaging são assinados automaticamente pelo TEC.Observability e exportáveis para qualquer coletor OpenTelemetry (Grafana, Datadog, Zabbix, Azure Monitor, etc.).
Feed do GitHub Packages (PAT com read:packages):
dotnet nuget add source https://nuget.pkg.github.com/tudoemcodigo/index.json -n tec-interno -u <usuario-github> -p <PAT>
# Núcleo e persistência
dotnet add package TEC.Messaging
dotnet add package TEC.Messaging.SqlServer # ou TEC.Messaging.PostgreSql
# Broker desejado
dotnet add package TEC.Messaging.RabbitMQ # ou TEC.Messaging.AzureServiceBus ou TEC.Messaging.Aws// 1. Contrato de integração (público, versionado) e o evento de domínio (interno)
[IntegrationEvent("vendas.pedido-aprovado", Version = 1)]
public sealed record PedidoAprovadoV1(Guid PedidoId, decimal Total);
public sealed record PedidoAprovado(Guid PedidoId, decimal Total, DateTimeOffset OccurredAt) : IDomainEvent;
// 2. Registro
builder.Services.AddTecMessaging()
.AddEventType<PedidoAprovadoV1>()
.Map<PedidoAprovado>(e => new PedidoAprovadoV1(e.PedidoId, e.Total))
.UseSqlServer<VendasDbContext>() // Outbox/Inbox no banco da aplicação
.UseRabbitMq(o => // URI no segredo "rabbitmq" do TEC.Vault
{
o.Exchange = "vendas.eventos";
o.RoutingKeyPrefixToRemove = "vendas."; // routing key = tipo sem o prefixo ("pedido-aprovado")
})
.AddConsumer<Faturamento>() // o consumidor do exemplo abaixo
.AddOutboxRelay();
builder.Services.AddDbContext<VendasDbContext>((sp, o) => o.UseSqlServer(conexao).UseTecMessaging(sp));
// 3. No modelo: modelBuilder.AddTecMessaging("vendas"); (e gere a migration)
// 4. No caso de uso: só altere o agregado. O evento vira mensagem no mesmo SaveChanges.
pedido.Aprovar(agora); // RaiseDomainEvent(new PedidoAprovado(...))
await db.SaveChangesAsync(ct);Consumidor idempotente:
public sealed class Faturamento(RabbitMqConsumerDependencies deps, IServiceScopeFactory scopes) : RabbitMqConsumer(deps)
{
protected override QueueDefinition Queue { get; } = new("financeiro.faturamento", ["pedido-aprovado"]);
protected override async Task<ConsumeResult> HandleAsync(ReceivedMessage message, CancellationToken ct)
{
await using var scope = scopes.CreateAsyncScope();
if (!await scope.ServiceProvider.GetRequiredService<IInboxStore>().TryBeginAsync(message.Envelope.MessageId, "faturamento", ct))
return ConsumeResult.Completed; // reentrega: já processada
// ... efeito ...
await scope.ServiceProvider.GetRequiredService<FinanceiroDbContext>().SaveChangesAsync(ct);
return ConsumeResult.Completed;
}
}Componentes e ligações do TEC.Messaging, gerados pelo GitDiagram a partir da main em 02/11/2026. O diagrama é gerado por IA a partir de uma amostra do código: use-o como mapa de leitura, não como contrato.
flowchart TD
subgraph group_core["Messaging core"]
node_domain["Domain events"]
node_contracts["Event contracts"]
node_envelopes["Message envelopes<br/>[MessageEnvelope.cs]"]
node_context["Correlation context<br/>[MessageContext.cs]"]
node_relay["Outbox relay<br/>[OutboxRelay.cs]"]
node_inbox["Inbox abstraction<br/>[IInboxStore.cs]"]
node_sanitize["PII sanitizer"]
node_diagnostics["Diagnostics"]
end
subgraph group_persistence["Persistence adapters"]
node_sqloutbox[("SQL Server stores")]
node_sqlinterceptor["SQL Server interceptor"]
node_pgoutbox[("PostgreSQL stores")]
node_pginterceptor["PostgreSQL interceptor"]
end
subgraph group_rabbit["RabbitMQ transport"]
node_rabbitpub["Rabbit publisher"]
node_rabbitconsumer["Rabbit consumer"]
node_rabbitadmin["DLQ administration"]
end
subgraph group_azure["Azure transport"]
node_azurepub["Azure publisher"]
node_azureconsumer["Azure consumer"]
node_azureconnection["Azure connection"]
end
subgraph group_aws["AWS transport"]
node_awspub["AWS publisher"]
node_awsconsumer["SQS consumer<br/>[AwsSqsConsumer.cs]"]
node_awsclients["AWS clients"]
end
node_application(("Application"))
node_application -->|"raises events"| node_domain
node_domain -->|"creates envelopes"| node_envelopes
node_contracts -->|"defines metadata"| node_envelopes
node_context -->|"adds correlation"| node_envelopes
node_application -->|"saves events"| node_sqlinterceptor
node_application -->|"saves events"| node_pginterceptor
node_sqlinterceptor -.->|"stores messages"| node_sqloutbox
node_pginterceptor -.->|"stores messages"| node_pgoutbox
node_sqloutbox -->|"provides messages"| node_relay
node_pgoutbox -->|"provides messages"| node_relay
node_relay -->|"publishes via"| node_rabbitpub
node_relay -->|"publishes via"| node_azurepub
node_relay -->|"publishes via"| node_awspub
node_rabbitconsumer -->|"deduplicates with"| node_inbox
node_azureconsumer -.->|"deduplicates with"| node_inbox
node_awsconsumer -.->|"deduplicates with"| node_inbox
node_rabbitconsumer -->|"supports DLQ ops"| node_rabbitadmin
node_azurepub -->|"gets sender"| node_azureconnection
node_azureconsumer -->|"gets client"| node_azureconnection
node_awspub -.->|"uses clients"| node_awsclients
node_awsconsumer -->|"uses clients"| node_awsclients
node_rabbitconsumer -->|"records activity"| node_diagnostics
node_azureconsumer -->|"sanitizes data"| node_sanitize
node_awsconsumer -->|"records activity"| node_diagnostics
classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_domain,node_contracts,node_envelopes,node_context,node_relay,node_inbox,node_sanitize,node_diagnostics toneBlue
class node_sqloutbox,node_sqlinterceptor,node_pgoutbox,node_pginterceptor toneAmber
class node_rabbitpub,node_rabbitconsumer,node_rabbitadmin toneMint
class node_azurepub,node_azureconsumer,node_azureconnection toneRose
class node_awspub,node_awsconsumer,node_awsclients,node_application toneIndigo
| Arquivo | O que responde |
|---|---|
| 📜 Contratos de integração | Como nomear e versionar eventos, mapear eventos de domínio, o envelope e o registro com AOT |
| 📤 Outbox | Como o evento entra no Outbox, como o relay publica, falhas, Dead e administração |
| 📥 Inbox | Como tornar o consumidor idempotente |
| 🗄️ SQL Server | Persistência com SQL Server (esquema, índices, UPDLOCK/READPAST, interceptor) |
| 🐘 PostgreSQL | Persistência com PostgreSQL (esquema, índices, SKIP LOCKED, RETURNING) |
| 🐇 RabbitMQ | Topologia, retry, DLQ, concorrência paralela, pausa e circuit breaker |
| ☁️ Azure Service Bus | Publicador e consumidor em tópicos e filas no Azure Service Bus |
| 🟧 AWS SQS / SNS | Publicador e consumidor com Amazon SQS e SNS |
| 📡 Telemetria | Spans, métricas, logs e correlação |
| 🛡️ Segurança e LGPD | Sanitização de PII, segredos no cofre, limites de payload e riscos residuais |
| 🧪 Testes | Categorias, testes unitários, de integração, carga e benchmarks |
| 🛠️ Desenvolvimento | Build, modo local, lock files e publicação |
| 🔍 Revisão técnica | Achados de segurança, bugs e organização (2026-10-19), correções aplicadas e melhorias sugeridas |
| 🏛️ Decisões Arquiteturais (ADRs) | Registros formais das decisões de arquitetura e engenharia do TEC.Messaging |
Também: 🧰 Samples · ⚙️ CI/CD · READMEs dos pacotes (SqlServer, PostgreSql, RabbitMQ, AzureServiceBus, Aws).
| Item | Suporte |
|---|---|
| .NET | 10 (LTS) |
| Native AOT | TEC.Messaging e TEC.Messaging.RabbitMQ (contratos com JsonTypeInfo); provedores EF Core e SDKs de nuvem marcam <IsAotCompatible>false</IsAotCompatible> |
| Bancos de Dados | SQL Server 2016+ / Azure SQL (OUTPUT, READPAST) e PostgreSQL 12+ (SKIP LOCKED, RETURNING) |
| Brokers | RabbitMQ 3.13+ e 4.x (filas quorum), Azure Service Bus (Standard/Premium), AWS SQS/SNS |
Sanitização de PII (LGPD) nos erros gravados no Outbox, nos cabeçalhos e motivos de DLQ e nos resumos do relay, credenciais de brokers resolvidas via TEC.Vault, limite de payload configurável (padrão 256 KiB, até 4 MiB) e correlação sanitizada. Detalhes, riscos residuais e checklist em docs/seguranca.md.
Unitários sem dependências; integração com SQL Server e RabbitMQ em container (PostgreSQL, Azure Service Bus e AWS têm testes unitários). Detalhes em docs/testes.md.
PR para a main com o ci-ok verde. Veja docs/desenvolvimento.md.
SemVer (em 0.x, mudanças incompatíveis podem ocorrer em versões MINOR); prévias X.Y.Z-preview.N a cada merge. Histórico no Changelog.
Apache 2.0 · Criado e mantido por Roberto Oliveira, equipe Tudo em Código · github.com/tudoemcodigo