.NET
Cloud Native e Cloud Agnostic
para rodar .NET em qualquer Cloud
ou sem Cloud sempre de forma profissional!
Últimas publicações
Aqui estão os últimos 12 posts de mais de 500…
OA01 – Por mais discriminadores de comportamento e menos enums
Existe um vício oculto em projetos .NET que poucos questionam: usar enums para representar status, tipos e categorias, e depois espalhar if e switch por toda a base de código com base em deduções de regras implícitas. if (grupo.Tipo == TipoGrupo.Administrador) { // pode acessar tudo } else if...
Oragon.RabbitMQ 1.6 – Welcome .NET 10
Desde a versão 1.1.0 (janeiro/2025), o Oragon.RabbitMQ passou por 6 releases com mais de 70 commits, resultando em +3.894 linhas adicionadas e -1.245 removidas ao longo de 112 arquivos. A seguir, um resumo das principais novidades. O que mudou de v1.1 para v1.6 Suporte a .NET 10 A partir da...
Agnostic Services, Hexagonal Architecture SEM DDD
Esta semana, me deparei com críticas à arquitetura Hexagonal (Ports and Adapters) fundamentadas na suposição equivocada de que seu uso está condicionado à aplicação de Domain-Driven Design (DDD). Como se fosse um privilégio reservado apenas a sistemas que alcançaram um suposto "grau de maturidade...
RabbitMQ: Filas efêmeras
Ao pensar em filas, é comum pensarmos em filas com um ciclo de vida muito longo, filas que existem enquanto a aplicação existir. É comum pensarmos em filas como recursos estáticos que fazem parte dos requisitos de funcionamento da aplicação, nascendo quando a aplicação é implantada em produção...
Respondendo: Até que ponto isolamento de Bases de Dados em Microsserviços faz sentido?
Recentemente, um aluno questionou no nosso fórum interno sobre abordagens como Database-server-per-service, Database-per-service, Schema-per-service e Private-tables-per-service no contexto de microsserviços. Esse é um tema recorrente quando o assunto é microsserviços e não há resposta...
Simulando I/O limitado com Docker: como testar aplicações sob restrições realistas
Testar aplicações é um desafio recorrente e complexo na engenharia de software. Porém, quando o cenário envolve restrições específicas de I/O — como latência de disco, throughput de leitura/escrita reduzido ou limitação de operações por segundo — o desafio ganha outra dimensão. Nesse contexto, a...
O perigo silencioso das conexões stateful: por que respeitar o ciclo de vida importa?
APIs HTTP dominam por décadas o paradigma de comunicação entre aplicações, é fácil cair na armadilha de tratar qualquer conexão como se fosse apenas mais uma requisição stateless, principalmente conexões stateful "sob HTTP". Mas nem toda conexão é igual. E conexões de longa duração merecem muito...
O marketing dos benchmarks
Já faz algum tempo que os benchmarks deixaram de ser apenas uma ferramenta técnica e passaram a ocupar espaço central no marketing de produtos e tecnologias. De linguagens de programação a frameworks web, de modelos de LLM a placas de vídeo, benchmarks são utilizados como argumentos de venda — e...
Mitigando os custos de Reflection
Reflection oferece um mecanismo sofisticado para inspeção e manipulação de metadados de tipos, métodos e propriedades em tempo de execução. No entanto, essa flexibilidade vem acompanhada de custos elevados, que penalizam desempenho e eficiência de qualquer aplicação. Sempre que podemos evitar,...
O custo real das exceptions – 02 – Estamos olhando para o lugar certo?
Sem sombra de dúvidas, o principal argumento que sustenta a ideia de que exceptions são ruins é o custo. Afinal, ninguém quer colar em si uma placa dizendo que "optou pelo caminho mais lento". Ao mesmo tempo, as discussões acaloradas transformam o debate em uma rinha onde cada qual adota uma...
O custo real das exceptions – 01 – A série
O uso de exceptions no .NET tem sido um tópico de debates acalorados na comunidade. Para alguns, exceptions representam um custo desnecessário, um vestígio de práticas antiquadas que poderiam ser substituídas por abordagens mais eficientes. Para outros, são um pilar essencial para a robustez e...
Vai criar um IConfiguration? Não esqueça as variáveis de ambiente!
Quando construímos aplicações em .NET (especialmente .NET Core e posteriores), temos acesso a um pipeline de configuração bastante poderoso, que permite combinar diversas fontes de configuração: arquivos JSON, variáveis de ambiente, User Secrets, Azure Key Vault, entre outras. Entretanto, não é...
Projetos Open Source
projetos ativos e projetos antigos disponíveis para estudo
OpenIdConnect Mock Server – Identity Server fake
Você já fez uma prova de conceito ou uma demonstração em que seria bem interessante testar com vários perfís, mas pensou duas vezes sobre o esforço de subir um Identity Server? Já se questionou quais alternativas teria e tentou de tudo não ter esse esforço? Pois bem, aconteceu comigo algumas...
RabbitMQ Walkthrough v1
RabbitMQ - Demonstração de Comportamento padrão com Mensageria. Material complementar dos cursos: RabbitMQ para Aplicações .NETDocker Definitivo / O ROADMAP Objetivo 1) Demonstrar comportamento padrão quando: A) Temos uma carga de trabalho menor que nossa capacidade de processamentoB) Temos uma...
mssql-server-linux | SQL Server +Automações
A mesma imagem do SQL Server no Linux, mas tão configurável quanto as consagradas imagens do MariaDB, MySQL e PostgreSQL. Quem precisa subir um banco de dados junto com a aplicação precisa de uma imagem que possibilite a criação de usuários, databases, inicialização via scripts. Esses recursos já...
Oragon.AspNetCore.Hosting.AMQP
Se olharmos com cuidado para o HTTP e AMQP conseguimos encontrar semelhanças das mais diversas. Headers, Body. Se olharmos sobre as implementações sob o HTTP que conhecemos, vemos também outras características comuns como Routing, parsing. Fato que usar a infraestrutura base do ASP.NET Core, com...
EnterpriseApplicationLog
Enterprise Application Log consiste é um stack pré-configurado que contém RabbitMQ e ELK Stack colaborando para entregar uma robusta plataforma de monitoramento, centralização e consolidação de logs. Nesse stack de log utilizo RabbitMQ, LogStash, ElasticSearch e Kibana com Docker Compose. São...
Entender | Analisar | Projetar | Desenvolver | Implantar | Manter
A segurança que você busca não está em um tutorialPara entender uma tecnologia é importante entender o que influenciou sua criação, o que ela faz de fato, como ela faz. Para que então se sinta seguro e confiante a respeito das decisões que está prestes a tomar.
De um lado precisamos compreender o que está sendo feito por baixo dos panos para descobrir como extrair o máximo de uma tecnologia ou, ao menos, não atrapalhar o bom funcionamento dela.
O Cloud Native .NET é uma jornada de descoberta sobre tecnologias e patterns que fazem parte da maioria dos softwares que usamos, que somos usuários e que suportam e toleram altas cargas de trabalho, de forma eficaz, eficiente e sustentável.
É primeiro entendendo o que eles fazem, que podemos descobrir oportunidades e evoluir no que fazemos…
Conteúdo
O perigo silencioso das conexões stateful: por que respeitar o ciclo de vida importa?
APIs HTTP dominam por décadas o paradigma de comunicação entre aplicações, é fácil cair na armadilha de tratar qualquer conexão como se fosse apenas mais uma requisição stateless, principalmente conexões stateful "sob HTTP". Mas nem toda conexão é igual. E conexões de longa duração merecem muito...
O marketing dos benchmarks
Já faz algum tempo que os benchmarks deixaram de ser apenas uma ferramenta técnica e passaram a ocupar espaço central no marketing de produtos e tecnologias. De linguagens de programação a frameworks web, de modelos de LLM a placas de vídeo, benchmarks são utilizados como argumentos de venda — e...
Mitigando os custos de Reflection
Reflection oferece um mecanismo sofisticado para inspeção e manipulação de metadados de tipos, métodos e propriedades em tempo de execução. No entanto, essa flexibilidade vem acompanhada de custos elevados, que penalizam desempenho e eficiência de qualquer aplicação. Sempre que podemos evitar,...
O custo real das exceptions – 02 – Estamos olhando para o lugar certo?
Sem sombra de dúvidas, o principal argumento que sustenta a ideia de que exceptions são ruins é o custo. Afinal, ninguém quer colar em si uma placa dizendo que "optou pelo caminho mais lento". Ao mesmo tempo, as discussões acaloradas transformam o debate em uma rinha onde cada qual adota uma...
O custo real das exceptions – 01 – A série
O uso de exceptions no .NET tem sido um tópico de debates acalorados na comunidade. Para alguns, exceptions representam um custo desnecessário, um vestígio de práticas antiquadas que poderiam ser substituídas por abordagens mais eficientes. Para outros, são um pilar essencial para a robustez e...
Vai criar um IConfiguration? Não esqueça as variáveis de ambiente!
Quando construímos aplicações em .NET (especialmente .NET Core e posteriores), temos acesso a um pipeline de configuração bastante poderoso, que permite combinar diversas fontes de configuração: arquivos JSON, variáveis de ambiente, User Secrets, Azure Key Vault, entre outras. Entretanto, não é...
Sobre MediatR
Nos últimos anos, o MediatR tornou-se amplamente utilizado na comunidade .NET. Muitos projetos são construídos a partir de templates que o incorporam, resultando em uma adoção generalizada. No entanto, poucas pessoas questionam a necessidade real de sua utilização. Em algumas ocasiões, fui...
Há momentos em que é preciso dizer NÃO para o negócio
No cenário atual de desenvolvimento de sistemas, especialmente em ambientes de alta demanda, a realidade se impõe: nem sempre é possível atender a todas as solicitações do negócio sem alterar a base técnica. Em diversas situações, é necessário que o arquiteto exerça a maturidade de saber quando...
Voltando do .NET Aspire para o Docker Compose
Já faz algum tempo que publiquei um post contando sobre minha experiência com o .NET Aspire. Depois de mais de 14 semanas com .NET Aspire chegou a hora de dizer “até logo”. Neste texto, vamos discutir a viabilidade de usar o .NET Aspire em projetos baseados em Docker Compose e também mostrar o...
Oragon.RabbitMQ 1.1 – Reduzindo Alocações
Então esse foi o dia que queimei a lingua! Uma das criticas que faço às publicações que falam sobre ganhos absurdos de 50%, 80% e até mais de 100% de performance onde todas as variáveis se mantiveram as mesmas, é que afinal: Ganhamos 100% ou deixamos de perder 50%? Como o velho ditado "A...
Microsserviços vs Componentes
Uma dúvida frequente na adoção de microserviços é sobre a diferenciação entre o “microserviços” e os “componentes” que ele pode conter. Muitas pessoas imaginam que um microserviço seja apenas uma API, implementada em um projeto monolítico, mas a realidade (e a prática) mostram que ele pode sim ser...
Implementando Delay no RabbitMQ com Oragon.RabbitMQ sem Delayed Exchange
Um assunto recorrente quando falamos de RabbitMQ é a implementação de delay. Embora tenhamos o delayed exchange, existem questões relacionadas a cluster que precisam ser consideradas e por isso não é algo que aborde, além de ser algo instalado à parte. Existem muitos motivos para evitarmos esse...
Conheça nosso Podcast
DevShow PodcastEm 2019 resolvemos criar um podcast, o DevShow Podcast, desde lá são mais de 40 episódios com muito assunto legal, sempre com essa pegada pessoal, falando coisas sérias, mas sem o menor compromisso com a formalidade.
.NET
OA01 – Por mais discriminadores de comportamento e menos enums
Existe um vício oculto em projetos .NET que poucos questionam: usar enums para representar status, tipos e categorias, e depois espalhar if e switch por toda a base de código com base em deduções de regras implícitas.
if (grupo.Tipo == TipoGrupo.Administrador)
{
// pode acessar tudo
}
else if (grupo.Tipo == TipoGrupo.Operador)
{
// pode acessar quase tudo
}
else if (grupo.Tipo == TipoGrupo.Visualizador)
{
// só leitura
}
Esse código parece inofensivo. Está limpo, compila, passa no code review. Mas ele carrega um problema estrutural que escala de forma destrutiva: o comportamento do sistema está implícito no código, não explícito no modelo.
Quem olha para o enum TipoGrupo não sabe quais permissões cada tipo carrega. Quem olha para o banco de dados não encontra essa informação. Quem precisa adicionar um novo tipo precisa caçar todos os if e switch espalhados pela aplicação para entender o que precisa implementar.
Isso não é modelagem. É arqueologia.
o problema real: enums escondem especificações inteiras
Um enum em C# é, na essência, um inteiro com um apelido, um nome. Ele não carrega semântica, não expressa capacidades, não declara limites. Toda a inteligência que deveria estar associada àquele valor vive em outro lugar — dispersa em condicionais, enterrada em services, duplicada entre controllers e workers.
Considere um sistema de pedidos com status:
public enum StatusPedido
{
Rascunho,
Confirmado,
EmProcessamento,
Enviado,
Entregue,
Cancelado
}
Parece completo. Mas esse enum não responde a nenhuma pergunta relevante sobre comportamento:
- Quais status permitem cancelamento?
- Em quais status o pedido pode ser editado?
- Quais status geram notificação ao cliente?
- Quais transições de status são válidas?
Essas respostas estão espalhadas pelo código. E cada desenvolvedor que precisa dessa informação faz a mesma jornada: abre o repositório, busca por StatusPedido, lê dezenas de arquivos, monta mentalmente o mapa de comportamentos, e torce para não ter esquecido nenhum caso.
Quando um status novo precisa ser adicionado — digamos, AguardandoRetirada — o desenvolvedor adiciona o valor no enum e… começa a caçada. Cada switch precisa ser revisado. Cada if que compara com StatusPedido.Enviado pode precisar incluir o novo status. E se algum for esquecido, o bug não aparece na compilação. Aparece em produção.
Isso viola frontalmente o Open/Closed Principle: o sistema deveria ser aberto para extensão e fechado para modificação. Adicionar um novo tipo ou status não deveria exigir alteração de código existente. Mas com enums, cada novo valor é uma cirurgia em toda a base de código.
Alternativa: Discriminadores de Comportamento
A solução não é complexa. É uma mudança de perspectiva na modelagem: em vez de tratar status e tipos como constantes opacas, modelamos como entidades que declaram seus próprios comportamentos.
Em vez disso:
Tabela: Pedidos
- Id
- StatusPedidoId (int, FK → ?)
- ...
Enum no código: StatusPedido { Rascunho = 1, Confirmado = 2, ... }
Fazemos isso:
CREATE TABLE StatusPedido (
Id INT PRIMARY KEY,
Nome VARCHAR(50) NOT NULL,
PermiteCancelamento BIT NOT NULL DEFAULT 0,
PermiteEdicao BIT NOT NULL DEFAULT 0,
NotificaCliente BIT NOT NULL DEFAULT 0,
ExigeAprovacao BIT NOT NULL DEFAULT 0,
Ativo BIT NOT NULL DEFAULT 1
);
INSERT INTO StatusPedido (Id, Nome, PermiteCancelamento, PermiteEdicao, NotificaCliente, ExigeAprovacao)
VALUES
(1, 'Rascunho', 1, 1, 0, 0),
(2, 'Confirmado', 1, 0, 1, 0),
(3, 'Em Processamento', 0, 0, 0, 0),
(4, 'Enviado', 0, 0, 1, 0),
(5, 'Entregue', 0, 0, 1, 0),
(6, 'Cancelado', 0, 0, 1, 0);
Agora cada status declara explicitamente o que permite e o que não permite. Não existe dedução. Não existe interpretação. A especificação está no dado, não no código.
O mesmo princípio para tipos: flags de comportamento
Voltando ao exemplo do tipo de grupo. Em vez de perguntar “este grupo é do tipo Administrador?”, perguntamos “este tipo de grupo possui a capacidade de administração?”:
CREATE TABLE TipoGrupo (
Id INT PRIMARY KEY,
Nome VARCHAR(50) NOT NULL,
Administrador BIT NOT NULL DEFAULT 0,
PodeGerenciarUsuarios BIT NOT NULL DEFAULT 0,
PodeAcessarRelatorios BIT NOT NULL DEFAULT 0,
PodeAlterarConfiguracoes BIT NOT NULL DEFAULT 0
);
INSERT INTO TipoGrupo (Id, Nome, Administrador, PodeGerenciarUsuarios, PodeAcessarRelatorios, PodeAlterarConfiguracoes)
VALUES
(1, 'Administrador', 1, 1, 1, 1),
(2, 'Operador', 0, 0, 1, 1),
(3, 'Visualizador', 0, 0, 1, 0),
(4, 'Suporte', 0, 1, 1, 0);
A diferença é sutil na estrutura, mas profunda no impacto. Agora, quando o sistema precisa saber se um grupo pode gerenciar usuários, a pergunta é direta:
if (grupo.Tipo.PodeGerenciarUsuarios)
{
// comportamento permitido
}
Não estamos mais perguntando quem o grupo é. Estamos perguntando o que o grupo pode fazer. Essa distinção elimina a necessidade de conhecer a lista de tipos para implementar lógica. O código se torna agnóstico ao tipo específico e reage às capacidades declaradas.
Quem se beneficia dessa abordagem?
Esse princípio deq ue “comportamentos não podem ser deduzidos” se aplica a qualquer entidade que carrega regras implícitas avaliadas por condicionais no código. Além de status e tipos, os candidatos mais óbvios:
Perfis e papéis (roles) — o clássico if (usuario.Role == "Admin") espalhado por dezenas de controllers. Flags como PodeAprovar, PodeExcluir, AcessaRelatorios tornam as capacidades do papel explícitas e extensíveis sem tocar em código.
Categorias de produto/serviço — quando a categoria define comportamento fiscal, logístico ou de precificação. Em vez de if (produto.Categoria == Categoria.Digital) para pular cálculo de frete, uma flag ExigeFrete na tabela de categorias.
Planos e níveis de assinatura (tiers) — if (plano == Plano.Premium) para liberar funcionalidades. Flags como PermiteExportacao, LimiteUsuarios, SuportePrioritario tornam cada plano autodescritivo.
Modalidades de pagamento — em vez de deduzir que boleto exige compensação assíncrona e cartão é síncrono, flags como CompensacaoAssincrona, PermiteEstorno, ExigeConfirmacao.
Tipos de documento/contrato — quando o tipo define fluxo de aprovação, validade, necessidade de assinatura digital. Flags como ExigeAssinatura, TemValidade, RequerAprovacao.
Severidades e prioridades (em sistemas de incidentes, tickets, alertas) — em vez de if (severidade == Severidade.Critica) para decidir se notifica por SMS, flags como NotificaSMS, EscalaAutomaticamente, BloqueiaDeploy.
Etapas de pipeline/workflow — qualquer sistema com fluxo de etapas onde cada etapa habilita ou bloqueia ações. Flags como PermiteRetrocesso, ExigeAnexo, GeraTarefa.
Tipos de evento em event-driven — quando o tipo do evento determina se ele exige retry, se é idempotente, se deve ir para dead letter após falha. Flags como Idempotente, RetryAutomatico, CriticoParaConsistencia.
O padrão é sempre o mesmo: se existe um if no código que avalia a identidade da entidade para deduzir um comportamento, esse comportamento deveria ser um discriminador explícito na tabela que define aquela entidade. A pergunta não é “quem é”, é “o que declara que pode fazer”.
Antes e Depois com C#
Antes: comportamento implícito, acoplado ao valor do enum
public class PedidoService
{
public async Task CancelarAsync(Pedido pedido)
{
// quem decidiu que esses status permitem cancelamento?
// onde está essa especificação?
// se um novo status surgir, quem vai lembrar de atualizar aqui?
if (pedido.Status != StatusPedido.Rascunho &&
pedido.Status != StatusPedido.Confirmado)
{
throw new InvalidOperationException(
"Pedido não pode ser cancelado no status atual.");
}
pedido.Status = StatusPedido.Cancelado;
await _repository.AtualizarAsync(pedido);
}
public async Task EditarAsync(Pedido pedido, DadosPedido dados)
{
// mesma lógica espalhada em outro lugar
if (pedido.Status != StatusPedido.Rascunho)
{
throw new InvalidOperationException(
"Pedido não pode ser editado no status atual.");
}
pedido.Aplicar(dados);
await _repository.AtualizarAsync(pedido);
}
public async Task NotificarClienteAsync(Pedido pedido)
{
// e aqui mais uma lista de status que "alguém sabe" que notifica
if (pedido.Status == StatusPedido.Confirmado ||
pedido.Status == StatusPedido.Enviado ||
pedido.Status == StatusPedido.Entregue ||
pedido.Status == StatusPedido.Cancelado)
{
await _notificacaoService.EnviarAsync(pedido);
}
}
}
Três métodos, três listas de status hardcoded, zero rastreabilidade. Se um status novo for adicionado ao enum, o compilador não reclama. O sistema simplesmente se comporta de forma errada — silenciosamente.
Depois: comportamento declarativo, dirigido por dados
public class StatusPedidoEntity
{
public int Id { get; set; }
public string Nome { get; set; }
public bool PermiteCancelamento { get; set; }
public bool PermiteEdicao { get; set; }
public bool NotificaCliente { get; set; }
public bool ExigeAprovacao { get; set; }
}
public class PedidoService
{
public async Task CancelarAsync(Pedido pedido)
{
if (!pedido.Status.PermiteCancelamento)
{
throw new InvalidOperationException(
$"Status '{pedido.Status.Nome}' não permite cancelamento.");
}
pedido.Status = await _statusRepository
.ObterPorNomeAsync("Cancelado");
await _repository.AtualizarAsync(pedido);
}
public async Task EditarAsync(Pedido pedido, DadosPedido dados)
{
if (!pedido.Status.PermiteEdicao)
{
throw new InvalidOperationException(
$"Status '{pedido.Status.Nome}' não permite edição.");
}
pedido.Aplicar(dados);
await _repository.AtualizarAsync(pedido);
}
public async Task NotificarClienteAsync(Pedido pedido)
{
if (pedido.Status.NotificaCliente)
{
await _notificacaoService.EnviarAsync(pedido);
}
}
}
O código encolheu, ficou mais legível e — o mais importante — parou de mentir. Não existem mais listas secretas de status que permitem ou proíbem ações. A verdade está na tabela. O código apenas pergunta.
O impacto em escala: da UI ao worker
Quando comportamentos estão declarados em dados, o benefício não se limita ao backend. Ele permeia toda a aplicação:
Na API, o endpoint que retorna um pedido pode incluir as capacidades do status atual. O frontend não precisa replicar lógica — recebe permiteCancelamento: true e renderiza ou esconde o botão.
No worker, o consumidor que processa eventos pode consultar flags do status para decidir se executa uma ação, sem carregar regras hardcoded que precisam ser sincronizadas com o backend.
Na documentação, a tabela de status com suas flags é, por si só, uma especificação funcional. Um product owner consegue ler a tabela e validar se os comportamentos estão corretos — sem ler código.
No onboarding, um desenvolvedor novo consulta uma tabela e entende imediatamente quais comportamentos cada status habilita. Não precisa fazer arqueologia em dezenas de arquivos para montar o mapa mental.
transições de status: o próximo passo natural
Se status declaram comportamentos, o passo seguinte é declarar também as transições válidas:
CREATE TABLE TransicaoStatusPedido (
StatusOrigemId INT NOT NULL,
StatusDestinoId INT NOT NULL,
ExigeAprovacao BIT NOT NULL DEFAULT 0,
PRIMARY KEY (StatusOrigemId, StatusDestinoId),
FOREIGN KEY (StatusOrigemId) REFERENCES StatusPedido(Id),
FOREIGN KEY (StatusDestinoId) REFERENCES StatusPedido(Id)
);
INSERT INTO TransicaoStatusPedido (StatusOrigemId, StatusDestinoId, ExigeAprovacao)
VALUES
(1, 2, 0), -- Rascunho → Confirmado
(2, 3, 0), -- Confirmado → Em Processamento
(3, 4, 0), -- Em Processamento → Enviado
(4, 5, 0), -- Enviado → Entregue
(1, 6, 0), -- Rascunho → Cancelado
(2, 6, 1); -- Confirmado → Cancelado (exige aprovação)
Agora a máquina de estados inteira é declarativa. Validar uma transição no código se reduz a uma consulta:
public async Task TransicionarAsync(Pedido pedido, int novoStatusId)
{
var transicao = await _transicaoRepository
.ObterAsync(pedido.Status.Id, novoStatusId);
if (transicao is null)
{
throw new InvalidOperationException(
$"Transição de '{pedido.Status.Nome}' para o status " +
$"destino não é permitida.");
}
if (transicao.ExigeAprovacao)
{
await _aprovacaoService.SolicitarAsync(pedido, novoStatusId);
return;
}
pedido.Status = await _statusRepository.ObterAsync(novoStatusId);
await _repository.AtualizarAsync(pedido);
}
Adicionar um novo status? Insere na tabela e define as transições. Zero alteração no código. Open/Closed honrado, não na teoria.
É para abolir o uso de Enums? Quando o enum ainda faz sentido
Seria desonesto dizer que enums não têm lugar. Eles são adequados quando o valor é genuinamente estático, sem comportamento associado e sem expectativa de extensão: dias da semana, unidades de medida, direções cardeais. Valores que são constantes universais, não regras de negócio disfarçadas.
O problema não é o enum como estrutura de dados. É o uso do enum como veículo de especificação implícita. Quando cada valor carrega consigo um conjunto de regras que só existem nos if do código, o enum deixou de ser uma constante e virou uma especificação escondida.
Checklist: como identificar enums que deveriam ser tabelas?
Se pelo menos uma das condições abaixo for verdadeira, o enum é candidato a ser promovido para tabela com flags de comportamento:
- Existe pelo menos um
ifouswitchno código que avalia o valor do enum para decidir um comportamento. - Adicionar um novo valor ao enum exige alteração em mais de um arquivo.
- O significado de cada valor do enum precisa ser explicado verbalmente para novos desenvolvedores — não é autoevidente.
- O mesmo enum é avaliado em camadas diferentes da aplicação (API, serviço, worker) com lógicas que precisam estar sincronizadas.
- O product owner ou analista de negócio não consegue validar as regras associadas ao enum sem ler código.
Se três ou mais dessas condições são verdadeiras, não é uma questão de preferência. É débito técnico ativo.
Conclusão
Enums em C# são uma ferramenta de representação, não de modelagem. Quando usamos enums para carregar regras de negócio, estamos escolhendo conveniência no momento da escrita em troca de obscuridade permanente na manutenção.
Cada if (entidade.Status == Status.XPTO) é uma regra de negócio que existe apenas na memória de quem escreveu aquele código. Quando essa pessoa sai do time, a regra vira folclore. Quando o sistema cresce, o folclore vira risco operacional.
Promover status e tipos para tabelas com comportamento declarado não é overengineering. É tornar explícito o que já existe — só que escondido nos lugares errados.
A pergunta que todo arquiteto deveria fazer ao revisar um modelo de domínio é simples: se eu apagar todo o código e olhar apenas para o banco de dados, consigo entender o que o sistema permite e o que ele proíbe?
Se a resposta é não, os dados estão incompletos. E nenhuma quantidade de if no código compensa um modelo que não se explica sozinho.
Oragon.RabbitMQ 1.6 – Welcome .NET 10
Desde a versão 1.1.0 (janeiro/2025), o Oragon.RabbitMQ passou por 6 releases com mais de 70 commits, resultando em +3.894 linhas adicionadas e -1.245 removidas ao longo de 112 arquivos.
A seguir, um resumo das principais novidades.
ler mais…
O perigo silencioso das conexões stateful: por que respeitar o ciclo de vida importa?
APIs HTTP dominam por décadas o paradigma de comunicação entre aplicações, é fácil cair na armadilha de tratar qualquer conexão como se fosse apenas mais uma requisição stateless, principalmente conexões stateful “sob HTTP”. Mas nem toda conexão é igual.
E conexões de longa duração merecem muito mais respeito.
ler mais…
O marketing dos benchmarks
Já faz algum tempo que os benchmarks deixaram de ser apenas uma ferramenta técnica e passaram a ocupar espaço central no marketing de produtos e tecnologias. De linguagens de programação a frameworks web, de modelos de LLM a placas de vídeo, benchmarks são utilizados como argumentos de venda — e com razão: números impressionam.
Mas como todo dado isolado, os benchmarks contam apenas parte da história. Os números são reais, mas o contexto em que esses números são obtidos está longe da realidade da maioria dos sistemas em produção. E é justamente aí que mora o problema.
ler mais…
Mitigando os custos de Reflection
Reflection oferece um mecanismo sofisticado para inspeção e manipulação de metadados de tipos, métodos e propriedades em tempo de execução. No entanto, essa flexibilidade vem acompanhada de custos elevados, que penalizam desempenho e eficiência de qualquer aplicação.
Sempre que podemos evitar, evitamos seu uso, mas e quando não podemos? Como podemos tratar ou mitigar esses prejuízos em desempenho?
Hoje vou apresentar uma abordagem que usei recentemente em um projeto recente.
ler mais…Arquitetura
OA01 – Por mais discriminadores de comportamento e menos enums
Existe um vício oculto em projetos .NET que poucos questionam: usar enums para representar status, tipos e categorias, e depois espalhar if e switch por toda a base de código com base em deduções de regras implícitas.
if (grupo.Tipo == TipoGrupo.Administrador)
{
// pode acessar tudo
}
else if (grupo.Tipo == TipoGrupo.Operador)
{
// pode acessar quase tudo
}
else if (grupo.Tipo == TipoGrupo.Visualizador)
{
// só leitura
}
Esse código parece inofensivo. Está limpo, compila, passa no code review. Mas ele carrega um problema estrutural que escala de forma destrutiva: o comportamento do sistema está implícito no código, não explícito no modelo.
Quem olha para o enum TipoGrupo não sabe quais permissões cada tipo carrega. Quem olha para o banco de dados não encontra essa informação. Quem precisa adicionar um novo tipo precisa caçar todos os if e switch espalhados pela aplicação para entender o que precisa implementar.
Isso não é modelagem. É arqueologia.
o problema real: enums escondem especificações inteiras
Um enum em C# é, na essência, um inteiro com um apelido, um nome. Ele não carrega semântica, não expressa capacidades, não declara limites. Toda a inteligência que deveria estar associada àquele valor vive em outro lugar — dispersa em condicionais, enterrada em services, duplicada entre controllers e workers.
Considere um sistema de pedidos com status:
public enum StatusPedido
{
Rascunho,
Confirmado,
EmProcessamento,
Enviado,
Entregue,
Cancelado
}
Parece completo. Mas esse enum não responde a nenhuma pergunta relevante sobre comportamento:
- Quais status permitem cancelamento?
- Em quais status o pedido pode ser editado?
- Quais status geram notificação ao cliente?
- Quais transições de status são válidas?
Essas respostas estão espalhadas pelo código. E cada desenvolvedor que precisa dessa informação faz a mesma jornada: abre o repositório, busca por StatusPedido, lê dezenas de arquivos, monta mentalmente o mapa de comportamentos, e torce para não ter esquecido nenhum caso.
Quando um status novo precisa ser adicionado — digamos, AguardandoRetirada — o desenvolvedor adiciona o valor no enum e… começa a caçada. Cada switch precisa ser revisado. Cada if que compara com StatusPedido.Enviado pode precisar incluir o novo status. E se algum for esquecido, o bug não aparece na compilação. Aparece em produção.
Isso viola frontalmente o Open/Closed Principle: o sistema deveria ser aberto para extensão e fechado para modificação. Adicionar um novo tipo ou status não deveria exigir alteração de código existente. Mas com enums, cada novo valor é uma cirurgia em toda a base de código.
Alternativa: Discriminadores de Comportamento
A solução não é complexa. É uma mudança de perspectiva na modelagem: em vez de tratar status e tipos como constantes opacas, modelamos como entidades que declaram seus próprios comportamentos.
Em vez disso:
Tabela: Pedidos
- Id
- StatusPedidoId (int, FK → ?)
- ...
Enum no código: StatusPedido { Rascunho = 1, Confirmado = 2, ... }
Fazemos isso:
CREATE TABLE StatusPedido (
Id INT PRIMARY KEY,
Nome VARCHAR(50) NOT NULL,
PermiteCancelamento BIT NOT NULL DEFAULT 0,
PermiteEdicao BIT NOT NULL DEFAULT 0,
NotificaCliente BIT NOT NULL DEFAULT 0,
ExigeAprovacao BIT NOT NULL DEFAULT 0,
Ativo BIT NOT NULL DEFAULT 1
);
INSERT INTO StatusPedido (Id, Nome, PermiteCancelamento, PermiteEdicao, NotificaCliente, ExigeAprovacao)
VALUES
(1, 'Rascunho', 1, 1, 0, 0),
(2, 'Confirmado', 1, 0, 1, 0),
(3, 'Em Processamento', 0, 0, 0, 0),
(4, 'Enviado', 0, 0, 1, 0),
(5, 'Entregue', 0, 0, 1, 0),
(6, 'Cancelado', 0, 0, 1, 0);
Agora cada status declara explicitamente o que permite e o que não permite. Não existe dedução. Não existe interpretação. A especificação está no dado, não no código.
O mesmo princípio para tipos: flags de comportamento
Voltando ao exemplo do tipo de grupo. Em vez de perguntar “este grupo é do tipo Administrador?”, perguntamos “este tipo de grupo possui a capacidade de administração?”:
CREATE TABLE TipoGrupo (
Id INT PRIMARY KEY,
Nome VARCHAR(50) NOT NULL,
Administrador BIT NOT NULL DEFAULT 0,
PodeGerenciarUsuarios BIT NOT NULL DEFAULT 0,
PodeAcessarRelatorios BIT NOT NULL DEFAULT 0,
PodeAlterarConfiguracoes BIT NOT NULL DEFAULT 0
);
INSERT INTO TipoGrupo (Id, Nome, Administrador, PodeGerenciarUsuarios, PodeAcessarRelatorios, PodeAlterarConfiguracoes)
VALUES
(1, 'Administrador', 1, 1, 1, 1),
(2, 'Operador', 0, 0, 1, 1),
(3, 'Visualizador', 0, 0, 1, 0),
(4, 'Suporte', 0, 1, 1, 0);
A diferença é sutil na estrutura, mas profunda no impacto. Agora, quando o sistema precisa saber se um grupo pode gerenciar usuários, a pergunta é direta:
if (grupo.Tipo.PodeGerenciarUsuarios)
{
// comportamento permitido
}
Não estamos mais perguntando quem o grupo é. Estamos perguntando o que o grupo pode fazer. Essa distinção elimina a necessidade de conhecer a lista de tipos para implementar lógica. O código se torna agnóstico ao tipo específico e reage às capacidades declaradas.
Quem se beneficia dessa abordagem?
Esse princípio deq ue “comportamentos não podem ser deduzidos” se aplica a qualquer entidade que carrega regras implícitas avaliadas por condicionais no código. Além de status e tipos, os candidatos mais óbvios:
Perfis e papéis (roles) — o clássico if (usuario.Role == "Admin") espalhado por dezenas de controllers. Flags como PodeAprovar, PodeExcluir, AcessaRelatorios tornam as capacidades do papel explícitas e extensíveis sem tocar em código.
Categorias de produto/serviço — quando a categoria define comportamento fiscal, logístico ou de precificação. Em vez de if (produto.Categoria == Categoria.Digital) para pular cálculo de frete, uma flag ExigeFrete na tabela de categorias.
Planos e níveis de assinatura (tiers) — if (plano == Plano.Premium) para liberar funcionalidades. Flags como PermiteExportacao, LimiteUsuarios, SuportePrioritario tornam cada plano autodescritivo.
Modalidades de pagamento — em vez de deduzir que boleto exige compensação assíncrona e cartão é síncrono, flags como CompensacaoAssincrona, PermiteEstorno, ExigeConfirmacao.
Tipos de documento/contrato — quando o tipo define fluxo de aprovação, validade, necessidade de assinatura digital. Flags como ExigeAssinatura, TemValidade, RequerAprovacao.
Severidades e prioridades (em sistemas de incidentes, tickets, alertas) — em vez de if (severidade == Severidade.Critica) para decidir se notifica por SMS, flags como NotificaSMS, EscalaAutomaticamente, BloqueiaDeploy.
Etapas de pipeline/workflow — qualquer sistema com fluxo de etapas onde cada etapa habilita ou bloqueia ações. Flags como PermiteRetrocesso, ExigeAnexo, GeraTarefa.
Tipos de evento em event-driven — quando o tipo do evento determina se ele exige retry, se é idempotente, se deve ir para dead letter após falha. Flags como Idempotente, RetryAutomatico, CriticoParaConsistencia.
O padrão é sempre o mesmo: se existe um if no código que avalia a identidade da entidade para deduzir um comportamento, esse comportamento deveria ser um discriminador explícito na tabela que define aquela entidade. A pergunta não é “quem é”, é “o que declara que pode fazer”.
Antes e Depois com C#
Antes: comportamento implícito, acoplado ao valor do enum
public class PedidoService
{
public async Task CancelarAsync(Pedido pedido)
{
// quem decidiu que esses status permitem cancelamento?
// onde está essa especificação?
// se um novo status surgir, quem vai lembrar de atualizar aqui?
if (pedido.Status != StatusPedido.Rascunho &&
pedido.Status != StatusPedido.Confirmado)
{
throw new InvalidOperationException(
"Pedido não pode ser cancelado no status atual.");
}
pedido.Status = StatusPedido.Cancelado;
await _repository.AtualizarAsync(pedido);
}
public async Task EditarAsync(Pedido pedido, DadosPedido dados)
{
// mesma lógica espalhada em outro lugar
if (pedido.Status != StatusPedido.Rascunho)
{
throw new InvalidOperationException(
"Pedido não pode ser editado no status atual.");
}
pedido.Aplicar(dados);
await _repository.AtualizarAsync(pedido);
}
public async Task NotificarClienteAsync(Pedido pedido)
{
// e aqui mais uma lista de status que "alguém sabe" que notifica
if (pedido.Status == StatusPedido.Confirmado ||
pedido.Status == StatusPedido.Enviado ||
pedido.Status == StatusPedido.Entregue ||
pedido.Status == StatusPedido.Cancelado)
{
await _notificacaoService.EnviarAsync(pedido);
}
}
}
Três métodos, três listas de status hardcoded, zero rastreabilidade. Se um status novo for adicionado ao enum, o compilador não reclama. O sistema simplesmente se comporta de forma errada — silenciosamente.
Depois: comportamento declarativo, dirigido por dados
public class StatusPedidoEntity
{
public int Id { get; set; }
public string Nome { get; set; }
public bool PermiteCancelamento { get; set; }
public bool PermiteEdicao { get; set; }
public bool NotificaCliente { get; set; }
public bool ExigeAprovacao { get; set; }
}
public class PedidoService
{
public async Task CancelarAsync(Pedido pedido)
{
if (!pedido.Status.PermiteCancelamento)
{
throw new InvalidOperationException(
$"Status '{pedido.Status.Nome}' não permite cancelamento.");
}
pedido.Status = await _statusRepository
.ObterPorNomeAsync("Cancelado");
await _repository.AtualizarAsync(pedido);
}
public async Task EditarAsync(Pedido pedido, DadosPedido dados)
{
if (!pedido.Status.PermiteEdicao)
{
throw new InvalidOperationException(
$"Status '{pedido.Status.Nome}' não permite edição.");
}
pedido.Aplicar(dados);
await _repository.AtualizarAsync(pedido);
}
public async Task NotificarClienteAsync(Pedido pedido)
{
if (pedido.Status.NotificaCliente)
{
await _notificacaoService.EnviarAsync(pedido);
}
}
}
O código encolheu, ficou mais legível e — o mais importante — parou de mentir. Não existem mais listas secretas de status que permitem ou proíbem ações. A verdade está na tabela. O código apenas pergunta.
O impacto em escala: da UI ao worker
Quando comportamentos estão declarados em dados, o benefício não se limita ao backend. Ele permeia toda a aplicação:
Na API, o endpoint que retorna um pedido pode incluir as capacidades do status atual. O frontend não precisa replicar lógica — recebe permiteCancelamento: true e renderiza ou esconde o botão.
No worker, o consumidor que processa eventos pode consultar flags do status para decidir se executa uma ação, sem carregar regras hardcoded que precisam ser sincronizadas com o backend.
Na documentação, a tabela de status com suas flags é, por si só, uma especificação funcional. Um product owner consegue ler a tabela e validar se os comportamentos estão corretos — sem ler código.
No onboarding, um desenvolvedor novo consulta uma tabela e entende imediatamente quais comportamentos cada status habilita. Não precisa fazer arqueologia em dezenas de arquivos para montar o mapa mental.
transições de status: o próximo passo natural
Se status declaram comportamentos, o passo seguinte é declarar também as transições válidas:
CREATE TABLE TransicaoStatusPedido (
StatusOrigemId INT NOT NULL,
StatusDestinoId INT NOT NULL,
ExigeAprovacao BIT NOT NULL DEFAULT 0,
PRIMARY KEY (StatusOrigemId, StatusDestinoId),
FOREIGN KEY (StatusOrigemId) REFERENCES StatusPedido(Id),
FOREIGN KEY (StatusDestinoId) REFERENCES StatusPedido(Id)
);
INSERT INTO TransicaoStatusPedido (StatusOrigemId, StatusDestinoId, ExigeAprovacao)
VALUES
(1, 2, 0), -- Rascunho → Confirmado
(2, 3, 0), -- Confirmado → Em Processamento
(3, 4, 0), -- Em Processamento → Enviado
(4, 5, 0), -- Enviado → Entregue
(1, 6, 0), -- Rascunho → Cancelado
(2, 6, 1); -- Confirmado → Cancelado (exige aprovação)
Agora a máquina de estados inteira é declarativa. Validar uma transição no código se reduz a uma consulta:
public async Task TransicionarAsync(Pedido pedido, int novoStatusId)
{
var transicao = await _transicaoRepository
.ObterAsync(pedido.Status.Id, novoStatusId);
if (transicao is null)
{
throw new InvalidOperationException(
$"Transição de '{pedido.Status.Nome}' para o status " +
$"destino não é permitida.");
}
if (transicao.ExigeAprovacao)
{
await _aprovacaoService.SolicitarAsync(pedido, novoStatusId);
return;
}
pedido.Status = await _statusRepository.ObterAsync(novoStatusId);
await _repository.AtualizarAsync(pedido);
}
Adicionar um novo status? Insere na tabela e define as transições. Zero alteração no código. Open/Closed honrado, não na teoria.
É para abolir o uso de Enums? Quando o enum ainda faz sentido
Seria desonesto dizer que enums não têm lugar. Eles são adequados quando o valor é genuinamente estático, sem comportamento associado e sem expectativa de extensão: dias da semana, unidades de medida, direções cardeais. Valores que são constantes universais, não regras de negócio disfarçadas.
O problema não é o enum como estrutura de dados. É o uso do enum como veículo de especificação implícita. Quando cada valor carrega consigo um conjunto de regras que só existem nos if do código, o enum deixou de ser uma constante e virou uma especificação escondida.
Checklist: como identificar enums que deveriam ser tabelas?
Se pelo menos uma das condições abaixo for verdadeira, o enum é candidato a ser promovido para tabela com flags de comportamento:
- Existe pelo menos um
ifouswitchno código que avalia o valor do enum para decidir um comportamento. - Adicionar um novo valor ao enum exige alteração em mais de um arquivo.
- O significado de cada valor do enum precisa ser explicado verbalmente para novos desenvolvedores — não é autoevidente.
- O mesmo enum é avaliado em camadas diferentes da aplicação (API, serviço, worker) com lógicas que precisam estar sincronizadas.
- O product owner ou analista de negócio não consegue validar as regras associadas ao enum sem ler código.
Se três ou mais dessas condições são verdadeiras, não é uma questão de preferência. É débito técnico ativo.
Conclusão
Enums em C# são uma ferramenta de representação, não de modelagem. Quando usamos enums para carregar regras de negócio, estamos escolhendo conveniência no momento da escrita em troca de obscuridade permanente na manutenção.
Cada if (entidade.Status == Status.XPTO) é uma regra de negócio que existe apenas na memória de quem escreveu aquele código. Quando essa pessoa sai do time, a regra vira folclore. Quando o sistema cresce, o folclore vira risco operacional.
Promover status e tipos para tabelas com comportamento declarado não é overengineering. É tornar explícito o que já existe — só que escondido nos lugares errados.
A pergunta que todo arquiteto deveria fazer ao revisar um modelo de domínio é simples: se eu apagar todo o código e olhar apenas para o banco de dados, consigo entender o que o sistema permite e o que ele proíbe?
Se a resposta é não, os dados estão incompletos. E nenhuma quantidade de if no código compensa um modelo que não se explica sozinho.
Oragon.RabbitMQ 1.6 – Welcome .NET 10
Desde a versão 1.1.0 (janeiro/2025), o Oragon.RabbitMQ passou por 6 releases com mais de 70 commits, resultando em +3.894 linhas adicionadas e -1.245 removidas ao longo de 112 arquivos.
A seguir, um resumo das principais novidades.
ler mais…
RabbitMQ: Filas efêmeras
Ao pensar em filas, é comum pensarmos em filas com um ciclo de vida muito longo, filas que existem enquanto a aplicação existir.
É comum pensarmos em filas como recursos estáticos que fazem parte dos requisitos de funcionamento da aplicação, nascendo quando a aplicação é implantada em produção pela primeira vez e somente deixando de existir somente quando a aplicação é desativada ou substituída.
Hoje vamos abordar filas que possuem um ciclo de vida diferente, um ciclo de vida absolutamente curto, filas que podem durar de poucos milissegundos e semanas, e eventualmente até meses ou anos.
Não adianta torcer o nariz. Muitos aplicativos que estão no teu celular hoje fazem uso desse recurso em seus backends.
ler mais…
Mitigando os custos de Reflection
Reflection oferece um mecanismo sofisticado para inspeção e manipulação de metadados de tipos, métodos e propriedades em tempo de execução. No entanto, essa flexibilidade vem acompanhada de custos elevados, que penalizam desempenho e eficiência de qualquer aplicação.
Sempre que podemos evitar, evitamos seu uso, mas e quando não podemos? Como podemos tratar ou mitigar esses prejuízos em desempenho?
Hoje vou apresentar uma abordagem que usei recentemente em um projeto recente.
ler mais…
Voltando do .NET Aspire para o Docker Compose
Já faz algum tempo que publiquei um post contando sobre minha experiência com o .NET Aspire. Depois de mais de 14 semanas com .NET Aspire chegou a hora de dizer “até logo”.
Neste texto, vamos discutir a viabilidade de usar o .NET Aspire em projetos baseados em Docker Compose e também mostrar o processo de migração do .NET Aspire de volta para o Docker Compose.
ler mais…Containers
Simulando I/O limitado com Docker: como testar aplicações sob restrições realistas
Testar aplicações é um desafio recorrente e complexo na engenharia de software. Porém, quando o cenário envolve restrições específicas de I/O — como latência de disco, throughput de leitura/escrita reduzido ou limitação de operações por segundo — o desafio ganha outra dimensão.
Nesse contexto, a capacidade de simular gargalos de I/O torna-se uma ferramenta poderosa para validar a resiliência, eficiência e tolerância a falhas da sua aplicação. Felizmente, o Docker oferece mecanismos para isso, permitindo configurar limites finos de I/O em containers.
Não é todo dia que você é exposto a esse tipo de necessidade, como fui recentemente. Entretanto, a capacidade de ter à mão, algo tão simples quanto poderoso, permite validar e experimentar problemas previsíveis, muito antes de sequer contratar uma infra em produção.
É mais do que validar um setup, é sobre entregar previsibilidade arquietural.
Vamos explorar como isso pode ser feito, por que é útil e quais problemas essa abordagem ajuda a evitar.
ler mais…
Voltando do .NET Aspire para o Docker Compose
Já faz algum tempo que publiquei um post contando sobre minha experiência com o .NET Aspire. Depois de mais de 14 semanas com .NET Aspire chegou a hora de dizer “até logo”.
Neste texto, vamos discutir a viabilidade de usar o .NET Aspire em projetos baseados em Docker Compose e também mostrar o processo de migração do .NET Aspire de volta para o Docker Compose.
ler mais…
.NET ASPIRE Dashboard Standalone como Container
Nessa sexta-feira dia 16 foi lançado o Dashboard do ASPIRE como uma implementação OTLP standalone, em uma imagem docker, pronta para uso.
Entenda as novidades nesse post.
ler mais…
Microsoft Artifact Registry (MAR) – Descobrindo imagens e tags
Ao longo da jornada de containers do novo .NET desde sua primeira versão (.NET Core), temos o docker hub e posteriormente o MCR servindo imagens docker para nossas aplicações e servidores.
Sempre foi chato buscar as tags disponíveis, nos fazendo voltar às documentações e papers que descrevem migração.
Hoje você descobrirá que o Microsoft Container Registry mudou de nome e como descobrir as tags das principais imagens docker.
ler mais…
OCR Minimal API | .NET 8
Já pensou subir um serviço, com um simples docker run e ter um OCR ilimitado disponível para seu sistema? Você pode usar, comercializar, e fazer absolutamente qualquer coisa com o OCR.
Você pode usar para leitura de documentos, validação de prints, e muito mais.
Pois bem, hoje falarei sobre um projeto que criei e pode te economizar tempo e dinheiro no seu próximo projeto.
ler mais…Mensageria
Oragon.RabbitMQ 1.6 – Welcome .NET 10
Desde a versão 1.1.0 (janeiro/2025), o Oragon.RabbitMQ passou por 6 releases com mais de 70 commits, resultando em +3.894 linhas adicionadas e -1.245 removidas ao longo de 112 arquivos.
A seguir, um resumo das principais novidades.
ler mais…
RabbitMQ: Filas efêmeras
Ao pensar em filas, é comum pensarmos em filas com um ciclo de vida muito longo, filas que existem enquanto a aplicação existir.
É comum pensarmos em filas como recursos estáticos que fazem parte dos requisitos de funcionamento da aplicação, nascendo quando a aplicação é implantada em produção pela primeira vez e somente deixando de existir somente quando a aplicação é desativada ou substituída.
Hoje vamos abordar filas que possuem um ciclo de vida diferente, um ciclo de vida absolutamente curto, filas que podem durar de poucos milissegundos e semanas, e eventualmente até meses ou anos.
Não adianta torcer o nariz. Muitos aplicativos que estão no teu celular hoje fazem uso desse recurso em seus backends.
ler mais…
Oragon.RabbitMQ 1.1 – Reduzindo Alocações
Então esse foi o dia que queimei a lingua!
Uma das criticas que faço às publicações que falam sobre ganhos absurdos de 50%, 80% e até mais de 100% de performance onde todas as variáveis se mantiveram as mesmas, é que afinal: Ganhamos 100% ou deixamos de perder 50%?
Como o velho ditado “A estatística é a arte de torturar os números até que eles confessem o que você quer demonstrar”.
Bem, eu não passei batido à minha própria critica! Hoje vou falar sobre a redução nas alocações de respostas.
ler mais…
Implementando Delay no RabbitMQ com Oragon.RabbitMQ sem Delayed Exchange
Um assunto recorrente quando falamos de RabbitMQ é a implementação de delay.
Embora tenhamos o delayed exchange, existem questões relacionadas a cluster que precisam ser consideradas e por isso não é algo que aborde, além de ser algo instalado à parte.
ler mais…
Mininal API para RabbitMQ – Oragon.RabbitMQ 1.0 foi lançado!
Na noite do dia 25 de Dezembro lancçamos a versão 1 do Oragon.RabbitMQ, uma implementação de Minimal Api’s para o consumo de filas do RabbitMQ.
Com ele temos a mesma experiência de minimal api’s para o consumo de filas.
ler mais…Conteúdo e Posicionamento
.NET + Cloud Native + Cloud Agnostic
.NET | DevOps | Microservices | Containers | Continuous Delivery
.NET muito além do .NET
O mínimo de infra que todo dev e/ou arquiteto deveria saber
Aplicações distribuídas e comunicação entre serviços (RabbitMQ / gRPC)
Containers, Docker e Kubernetes
RabbitMQ e Mensageria e comunicação assíncrona entre aplicações e serviços
Arquitetura de Software e Arquitetura de Solução com foco no melhor aproveitamento em projetos .NET
Nossos números
Desde 2002 trabalhando com desenvolvimento de software
Desde 2002 ajudando outros devs
Desde 2010 trabalhando exclusivamente como arquiteto
Contas atingidas no telegram/facebook
Alunos
Microsoft MVP
Conteúdo Gratuito
Tudo que está aqui no gaGO.io é conteúdo gratuito, feito para ajudar desenvolvedores dos mais variados níveis.
Cursos
Tenho também alguns programas de acompanhamento. Esses programas tem a função de ajudar desenvolvedores em áreas específicas ou de forma mais abrangente na jornada do arquiteto.






















