fbpx
Oragon.RabbitMQ – A relação entre Minimal API’s e Agnostic Services

Oragon.RabbitMQ está no forno!

Dando continuidade à nossa série, abordarei a ideia central pelo qual adoto Minimal API’s e por que elas tem tanto poder na modelagem.

É hora de abrir a caixa de pandora e falar sobre modelagem de API’s agnósticas.

Contexto

Quando comecei a desenvolver minhas primeiras Web API’s, lá por volta de 2004 ~ 2006, já estava habituado com ao menos 3 camadas claras e rígidas.

Sem bairrismos

Graças à minha experiência anterior com Delphi, ASP, JScript, VBScript, VB .NET e C#, minha taxa de bairrismo e ranço a respeito de outras linguagens e plataformas, sempre beirou zero.

Foi assim que olhei para o lado, mirei no Java, concorrente direto e mais velho.

Intuitivamente acreditei estar claro que passaríamos pelos mesmos dilemas do Java aqui no .NET. Essa intuição me ajudou a ir mais rápido e mais longe, antes da maioria. Simplesmente por entender que o que viveríamos aqui, outras comunidades já haviam vivenciado, em especial a comunidade Java já havia vivenciado.

Assim tive mais tempo para me adaptar. Bastava entender a direção e me acostumar com o que era disruptivo. Então ao invés de supor que tinha um papel em branco, tendo de escolher entre todos os assuntos possíveis para estudar, baseado somente no meu gosto e opinião, optei por pegar um caminho diferente e olhar o que já havia sido trilhado na comunidade java.

Pois aqui não seria muito diferente.

E deu certo!

Dessa forma nunca perdi muito tempo torcendo o nariz, .NET era minha plataforma favorita, mas sempre olhei o Java como um primo mais velho que sempre teria algo a nos ensinar.

Eu sabia que era necessário ter certo discernimento para entender o que era erro de design e o que não era. Mas eu começando a desenvolver esse olhar. Mas, certamente, eles como comunidade, plataforma e arquitetura, estavam anos na nossa frente.

O incômodo

Por volta dessa época, algo que sempre me incomodou nas documentações e material técnico sobre Web API’s é o acoplamento.

A gente aprendia desde cedo que acoplamento é sempre negativo!

Um campo de estudo que já me cativava naquela época eram as aplicações distribuídas, e a compreensão de que poderíamos distribuir aplicações e não necessariamente usar HTTP como endpoint se tornava natural.

Naquela época tínhamos Remoting, MSMQ, Enterprise Services. Pela proximidade com o Windows já tínhamos componentes COM+ no passado, então compreender esses desenhos era algo importante para mim naquela época.

Como repito, sempre me pergunto:

  • O que os profissionais fazem?
  • Como eles pensam?
  • Por que pensam dessa forma?
  • Por que escolhem essa abordagem e tecnologia?
  • O que há aqui que não estou vendo?

Esse ceticismo me permitiu buscar respostas ao invés de criar pré-concepções burras e rasas.

Assim, com o passar do tempo, ver um DataSet do ADO.NET, uma Session do NHibernate, ou um dbml do Linq To SQL, ou mesmo um DbContext do Entity Framework em uma página ASPX (Webforms) sempre me gerou imenso desconforto.

Spring .NET me ensinou coisas incríveis…

Como citei em Sobre Ports and Adapters, Agnostic Services e modelagem de Serviços, meu contato com a versão .NET do Spring me permitiu compreender injeção de dependência, de uma forma pura. Muito diferente do mecanismo adotado no ASP.NET com as ServiceCollections.

Naquela época, 2006-2008, aqui na comunidade .NET estava nascendo um projeto chamado Castle.Windsor, um container de injeção de dependência baseado em Register/Resolver, um padrão com qual já briguei muito no passado.

Como se não bastassem as limitações do pattern e a discussão sobre ser ou não anti-pattern, há mais de 10 anos sinalizamos quão nocivo é a abordagem de register/resolver para a modelagem.


Referências

IoC e Dependency Injection – Os erros comuns de 2014

Register/Resolver e suas implicações para a modelagem e reaproveitamento de 2020


Com o Spring .NET, pude ver como realizar implementações de Serviços no Backend que pudessem ser expostos por diversas tecnologias, usando o máximo de performance na comunicação binária com Remoting, a versatilidade do Json, ou a compatibilidade do XML/WebService, a partir de somente um código, era incrível.

Estou falando de uma época pré-WCF, que de forma prática, tornou burocrática a mesma abordagem.

Com Spring.NET, só precisávamos construir um assembly com interfaces e dto’s, e disponibilizar para nossos clientes.

A exposição do host e o consumo no client eram abstraídos por simples configurações.

A chegada do WCF

O WCF trouxe uma necessidade de atributos de marcação nessas mesmas interfaces e DTO’s.

Isso fez com que sujássemos nossas entidades no WCF, diferente do Spring.NET, onde não havia essa demanda. Além disso a necessidade de configuração específica no WCF era muito maior, e juntar os 2 gerava um esforço cognitivo alto.

A abordagem do Spring.NET, onde você escreveria apenas uma vez e hospedaria e consumiria em diversos protocolos é herança do conceito de Java bean.

O Spring cuidava de criar proxies para nossas classes, dinamicamente. Tanto par hospedar quanto para consumir.

Então uma instância de nossa classe era envelopada por um proxy que lidava com a complexidade de comunicação remota, tornando o desenvolvimento local ou remoto, IDENTICO!


Hoje você pode ver algo parecido com o Refit, entretanto Refit se prende ao HTTP, enquanto a ideia do Spring.Services (pacote que lida com essas abstrações) é possibilitar usarmos múltiplos protocolos simultaneamente.

Só que naquela época, nenhum detalhe via atributo era necessário.


Agnostic Service

O pattern que permite criarmos serviços que não dependem de nenhuma tecnologia de exposição de serviços (wcf, webservice, webhttp, webapi, grpc, amqp etc), e podem ser expostos em diversos protocolos e tecnologias, antigos ou posteriores à sua criação, é o agnostic service.

A ideia é que você tenha classes simples que não misturem conceitos nem dependências específicas do modo de exposição.

Então a implementação abaixo, por exemplo, poderia se exposta por uma dúzia de protocolos.

public interface IEmailService
{
    Task SendEmailAsync(EmailCommand command);
}

public class EmailService: IEmailService
{
    public async Task SendEmailAsync(EmailCommand command)
    {
        ... send email using smtp ...
    }
}

A mágica é que a infraestrutura de injeção de dependência é quem precisa saber como criar o proxy, ou a instância diretamente.

Nesse cenário, de email, onde o feedback não precisa acontecer imediatamente, o serviço de email pode até estar offline, caso adote uma estratégia de comunicação via mensageria.

O poder dessa abordagem é que você tem um código abaixo:

public class OtherService (IEmailService emailService)
{
    public async Task DoSomething(EmailCommand command)
    {
      ...
      emailService.SendEmailAsync(new EmailCommand(){ ... });
      ...
    }
}

Mas quais são as vantagens?

De um lado, você reduz o acoplamento entre o serviço de sua tecnologia de hospedagem.

Também temos o aumento da capacidade de reaproveitar de código, sem necessidade de copy-and-paste ou reescrita.

A decisão sobre a topologia está fora do código, fora da implementação e isso reduz o esforço cognitivo.

Essa abordagem delega a decisão sobre topologia para um momento posterior ao desenvolvimento, sem afetar o desenvolvimento.

Também tira do radar decisões sobre topologia, permitindo que o desenvolvimento e construção da funcionalidade não dependa de uma decisão dessas.

No mundo “real”, essa decisão gera um compromisso de longo prazo, algo que se mudar, gera esforço de codificação, gera custo e relativo trabalho.

Com essa abordagem, não. É simples como quem troca de roupa.

Essa decisão pode ser tomada mais tarde e pode ser alterada com pouco esforço e zero mudança no código.

A abordagem permite, o que na minha opinião é o melhor benefício: não precisar tomar a decisão sobre a estratégia de distribuição até que fosse realmente necessário distribuir, mas também permitindo reagir rápido se necessário, sem necessidade de reconstrução.

De um lado estimativas descabidas, abaixo da realidade, de outro muito acima do esperado. E todas ignoravam eventos casuais que produziam aumento significativo de tráfego.

A maioria das abordagens tradicionais demandaria dias ou até semanas.

Minimal API’s são os proxies dinâmicos do passado

Com o Spring.NET, o pacote Spring.Services cuidava de criar um proxy para hospedar nossos serviços.

Agora, quase 2 décadas depois, as Minimal API’s podem ser um bom substituto para o antigo proxy AOP ou DynamicProxy, usado nesses casos.

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapPost("/",  (EmailCommand command, 
                  [FromServices]IEmailService emailService) 
                  => emailService.SendEmailAsync(command));“

record EmailCommand(string ToAddress, string Text);

A abordagem adotada no Oragon.RabbitMQ é focada em desburocratizar o consumo de mensagens, sem perder a essência do RabbitMQ.

Enquanto a maioria das abordagens ignora ou permite ignorar, questões importantes do message broker, essa abordagem entrega uma versão projetada e otimizada pare os recursos do RabbitMQ.

O resultado é que a abordagem abaixo permite reaproveitar esse serviço de envio de email via HTTP e AMQP.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<IAMQPSerializer, SystemTextJsonAMQPSerializer>();

builder.Services.MapQueue<IEmailService, EmailCommand>(config => config
    .WithDispatchInRootScope()    
    .WithAdapter((svc, msg) => svc.SendEmailAsync(msg))
    .WithQueueName("mail“")
    .WithPrefetchCount(1)
);

var app = builder.Build();

app.MapPost("/",  (EmailCommand command, 
                  [FromServices]IEmailService emailService) 
                  => emailService.SendEmailAsync(command));


record EmailCommand(string ToAddress, string Text);

O mesmo código: multiplos endpoints.

Exposto de diversas formas, com muita simplicidade e elegância: ao mesmo tempo.

Estamos falando da criação de serviços que não fazem ideia de que estão inseridos em um contexto web ou amqp (RabbitMQ). Dessa forma eles são mais fáceis de se compreender, de manter, de evoluir e custam mais barato.

Essa abordagem não se esconde em uma implementação em um proxy, em vez disso criamos simples funções de adaptação, como nas linhas 7 e 16.

Claro que se você leu o texto, encontrará diferença entre o que eu fazia lá em 2006 e o que estamos entregando agora.

Naquela época aceitávamos grandes arquivos de configuração, o web.config já era um desses arquivos gigantes, hoje os tempos são outros, e a abordagem queridinha mudou.

Mas o conceito, continua e a essência do poder dos Serviços Agnósticos continua a mesma!

O Cloud Native .NET é meu principal projeto.

Onde empenho energia para ajudar, acompanhar, direcionar Desenvolvedores, Líderes Técnicos e jovens Arquitetos na jornada Cloud Native.

Conduzo entregando a maior e mais completa stack de tecnologias do mercado.

Ao trabalhar com desenvolvedores experientes, eu consigo usar seu aprendizado com .NET, banco de dados, e arquitetura para encurtar a jornada.

Ao restringir à desenvolvedores .NET eu consigo usar do contexto de tecnologias e problemas do seu dia-a-dia, coisas que você conhece hoje, como WCF, WebForms, IIS e MVC, por exemplo, para mostrar a comparação entre o que você conhece e o que está sendo apresentado.

É assim que construímos fundamentos sólidos, digerindo a complexidade com didática, tornando o complexo, simples.

É assim que conseguimos tornar uma jornada densa, em um pacote de ~4 meses.

Eu não acredito que um desenvolvedor possa entender uma tecnologia sem compreender seus fundamentos. Ele no máximo consegue ser produtivo, mas isso não faz desse desenvolvedor um bom tomador de decisões técnicas.

É preciso entender os fundamentos para conseguir tomar boas decisões.

0 comentários

Enviar um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

[.net de a a z]

Lives

Fique de olho nas lives

Fique de olho nas lives no meu canal do Youtube, no Canal .NET e nos Grupos do Facebook e Instagram.

Aceleradores

Existem diversas formas de viabilizar o suporte ao teu projeto. Seja com os treinamentos, consultoria, mentorias em grupo.