fbpx
Spring.NET o Renascimento
Publicado em: terça-feira, 27 de fev de 2018

Quem me acompanha, principalmente já viu ou participou de alguma solução minha na última década, sabe que o Spring.NET é meu fiel escudeiro. Há motivos de sobra para não me desapegar do projeto, no entanto recorrentemente testo novas alternativas. Entretanto, mais de uma década após os primeiros flertes, ainda é meu container favorito, e por isso merece uma menção honrosa não só no blog, mas na minha carreira como um todo. Muito do que fiz com .NET nos últimos anos não seria economicamente viável sem ele.

O que vem a ser Spring.NET?

O Spring é um conceituado framework de aplicação do Java suportado e mantido pela SpringSource. A versão .NET do framework não é um port simples, como eles mesmos chamam, é um port espiritual. A diferença mora no reaproveitamento de código entre as baselines: Simplesmente não há! Inspirado no Java, Mark Pollack, seu fundador, criou toda a baseline de código do zero, sem migração direta, o que ajuda na compreensão de que é um framework .NET inspirado em um caso de sucesso do Java.

Embora seja composto por mais de 15 módulos, como mostra a imagem abaixo. Vamos abordar os dois principais módulos que dão suporte a todos os demais.

O projeto conta com módulos para Acesso a Dados(Nhibernate, ADO, etc), Mensageria (MSMQ, AMQP, Tibco, etc), Serviços (Enterprise Services, Web Services, Remoting, Wcf), MVC (Da versão 1 a 5), além do suporte a Agendamentos com Quartz.NET e Testes com NUnit e MSTest.

As categorias que encontramos são: CoreData AccessWebServicesIntegration, você pode encontrar a lista completa na documentação oficial, essa lista de módulos é baseada em 2 módulos principais: Spring.Core e Spring.AOP. É sobre esses dois módulos que irei discorrer nesse post.

Spring.Core

O Spring.Core é o módulo principal onde reside o Container IoC,  e toda a infraestrutura de Injeção de Dependência, hooks e abstrações que dão suporte aos demais módulos. É o coração do projeto no que diz respeito à IoC e DI. É um rico substituto para o Ninject, Castle Windsor, StructureMap e demais containers da lista.

Spring.AOP

O Spring.AOP enriquece o Spring.Core, trazendo consigo a infraestrutura de AOP, baseada no AopAlliance, possibilitando que todo o processo de configuração de AOP seja feito direto no container, reduzindo o acoplamento e necessidade de redesign ou interferência no design. Com o mínimo de respeito ao SOLID, é possível utilizar infraestrutura de IoC, DI e AOP sem que haja necessidade de recompilar nada, criando um leque de possibilidades infinitas.

O que o torna diferente?

Na prática se você sabe o que é um container IoC / DI e faz ideia do que um container faz, sabe que não é tarefa fácil. Desde a gestão do grafo de dependências, pipeline de inicialização, etc. Há diversos elementos complexos envolvendo todas essas tarefas, mas se estivermos falando de implementações simplistas, aí sim temos um pipeline mais curto mesmo.

Enquanto escrevia esse post, revisei as principais implementações de containers e me surpreendi positivamente. Da época em que eu fiz os posts IoC e Dependency Injection – Os erros comuns e IOC / DI – Você está fazendo isso errado! reclamando das principais implementações até hoje, muita coisa mudou. Os principais containers já oferecem formas de tornar a injeção de uma dependência eventualmente declarativa, e alguns vão além, possibilitando a definição de dependências nomeadas e padrão por tipo, o que é ótimo por atender as 2 necessidades.

Configuração

O Spring.NET traz consigo a flexibilidade de um modelo de configuração 100% baseado em XML’s. Um inferno para muitos, adorado por outros tantos. O modelo de configuração baseado em XML, oferece uma abstração nítida e uma visão clara de como cada instância está sendo criada. Os elementos, explícitos, apresentando classes, propriedades e construtores, cria uma imersão sem igual, permitindo encará-lo como um repositório estático de instâncias configuradas. Esse viés, faz com que objetos muito complexos, possam ser criados sem muito esforço, exigindo apenas configuração adequada.

Exemplo 1

A criação de um objeto, sem preencher nenhuma propriedade, utilizando construtor default (não parametrizado).

<object id="contextListenerObject" type="Spring.Context.ContextListenerObject, Spring.Core.Tests"/>

Exemplo 2

Uma configuração de uma instância de Spring.Objects.TestObject, que está no assembly Spring.Core.Tests, onde estamos criando essa instância utilizando o construtor de 2 parâmetros: Name e Age, respectivamente preenchidos com Rick e 30.

<object id="objectWithCtorArgValueShortcuts" type="Spring.Objects.TestObject, Spring.Core.Tests" singleton="false">
    <constructor-arg name="name" type="string" value="Rick" />
    <constructor-arg name="age" type="int" value="30" />
</object>

Exemplo 3

Semelhante ao exemplo 2, mas agora utilizando utilizando o construtor default, sem parâmetros, e preenchimento as propriedades com os mesmos valores.

<object id="testObject" type="Spring.Objects.TestObject, Spring.Core.Tests">
    <property name="Name" value="Rick"/>
    <property name="Age" value="30"/>
</object>

Exemplo 4

Eu não vou pesar e mostrar algo muito complexo, mas aqui vai um exemplo intermediário, com referências (ref=”…”), com objetos aninhados (QueueTransition.ConsumerCountManager está sendo preenchido com um ConsumerCountmanager, que por sua vez está sendo criado com o construtor não parametrizado, mas suas propriedades estão sendo preenchidas.

<object type="DevWeek.Architecture.Workflow.QueuedWorkFlow.QueuedTransition, DevWeek.Services">
    <property name="Origin" value="RequestStored" />
    <property name="Destination" value="MetadataDownloaded" />
    <property name="LogicalQueueName" value="MetadataDownloader" />
    <property name="ExchangeName" ref="CONFIG:DevWeek:RabbitMQ:DownloadPipeline:Exchange" />
    <property name="ConsumerCountManager" >
        <object type="DevWeek.Architecture.MessageQueuing.ConsumerCountManager, DevWeek.Services">
            <property name="MinConcurrentConsumers" value="1" />
            <property name="MaxConcurrentConsumers" value="10" />
            <property name="AutoscaleFrequency" value="00:01:00" />
            <property name="MessagesPerConsumerWorkerRatio" value="1" />
        </object>
    </property>
    <property name="ServiceMethod" value="ExecuteAsync" />
    <property name="Service">
        <object type="DevWeek.Services.Downloader.MetadataDiscoveryPipelineActivity, DevWeek.Services" autowire="constructor"></object>
    </property>
    <property name="ErrorFlowStrategy" value="SendToErrorQueue" />
</object>

Esse modelo desacoplado permite que você faça coisas incríveis, desconectado do seu domínio.

Sobre o port

Spring é mantido pela SpringSource, no entanto não há muito empenho no github já faz algum tempo. Questões como “spring.net is dead” são recorrente nos fóruns então resolvi fazer alguma coisa. Antes de ver o projeto morrer, pois aparentemente está em coma profundo. Resolvi fazer um fork e trabalhar na migração AS-IS para .NET Core. A dinâmica adotada é simplista: Recriar os principais projetos ignorando dependências não portadas, para essas vamos criar fakes. A principal meta era garantir a completude da migração, sem mudar nada no código do projeto. Mantendo o que já estava funcionando, e garantindo, com fakes/mocks que o projeto funcionaria. Na medida que conseguisse compilar o projeto com .NET Standard, revisitaria os fakes, para somente então tomar decisões mais profundas, que provavelmente mudam características do projeto. Mas o mindset é sempre fazer modificações ao redor do projeto, evitando contundência nas alterações do core sem que antes se tenha um baseline de testes extenso, para dar suporte.

Fakes

Assim comecei a migração dos projetos Spring.Core e Spring.Aop e obtive sucesso em menos de 6 horas de esforço. Mocks/Fakes foram criados para dar suporte a dependência de Common.Logging, System.EnterpriseServices e System.Runtime.Remoting. Hoje, Common.Logging já está presente no .NET Core via .NET Standard, portanto pude simplesmente excluir meu fake, adicionar a dependência do pacote nuget, e o projeto voltou a usar o Common Logging.

Quer entender melhor o que é um fake? Olhe isso. Um fake é uma implementação falsa de algo. Em vez de ter o comportamento esperado, o fake, assim como um mock, garante entradas e saídas. A utilização de fakes me permite não precisar mudar sequer uma linha no código do projeto alvo. Isso acontece por que o projeto a ser migrado depende de alguém que não está disponível na nova tecnologia/plataforma. É aí que mora o problema: Abstrair, substituir/refatorar ou desistir?

Muitas vezes não conseguimos ter a visão do esforço total pois esbarramos em uma ou outra dependência. No caso do spring .net o ponto de maior fricção nos 2 projetos que me dispus a migrar foi o Common.Logging. Mas não era possível saber o que mais iria apresentar problemas decorrentes da mudança do .NET Framework para .NET Core. O empenho de fakes me permitiu percorrer todo o caminho deixando uma migalha de pão, consistente de forma a tornar claro quais eram as dependências críticas. Aqui está o primeiro fake do Common.Logging, que já foi removido do projeto, na medida que a equipe do projeto concluiu seu port para .NET Standard.

Era possível mudar a implementação desse fake para que utilizasse a infraestrutura de logging do .net core. Eu não optei por isso. Mas se o fizesse, aconteceria de forma precisa e central, transformando o fake em um wrapper ou um adapter, criaria uma issue, para que outro momento removêssemos definitivamente o fake.

Já nos 2 projetos de teste, foi necessário recorrer ao nsubstitute para conseguir entregar funcionalidades compatíveis às entregues pelo Rhino.Mocks. Outro ponto curioso desse job foi que a migração dos 2 projetos de teste foi imensamente mais custosa do que a migração dos respectivos projetos principais.

O mindset

Migrar somente o Spring.Core e Spring.Aop viabiliza a utilização do Spring.NET. Todos os demais pacotes e projetos podem ser repensados, e na minha visão, não é responsabilidade de quem mantém o Spring.NET. Acredito que o Spring .NET possa se ater a esses 2 projetos, e ainda mais, acredito que Spring.Aop possa ser incorporado ao Spring.Core. Acredito também que com a ausência desses projetos adicionais, menos usados, a comunidade se engaje para lançar projetos similares, compondo e dando vida ao Spring .NET. Não vejo com bons olhos o projeto principal ser responsável, também, por gerenciar integrações com os mais diversos frameworks, como NH, EF, MVC/WEBAPI etc. Não acredito nesse big elephant, definitivamente não é algo que enxergue com bons olhos.

Conclusão

Por fim, estou retomando o projeto, com esse escopo reduzido. Hoje os testes já foram migrados, no entanto a dependência do Rhino.Mocks me fez criar um fake que na prática consiste em um wrapper/adapter para o NSubstitute. Nesse momento estou evitando fazer mudanças no core, mas por uma questão de sanidade.

O projeto seguirá, vou trazer mais coisas do projeto original, como documentação, por exemplo. E conto com a ajuda de quem quiser participar. Vale a pena ressaltar que para isso é necessário concordar e estar comprometido com a estratégia, além de saber muito bem o que se está fazendo.

Bom, por hoje é só….

[youtube https://www.youtube.com/watch?v=qwmoZTljVN0&w=320&h=192]

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.

4 Comentários

  1. brunohbrito

    Tem algum projeto Git teu com um exemplo simple com o spring?

    Responder
  2. FABIO HENRIQUE GABRIELE

    Enviei um PR com documentação do exemplo com asp.net core e criei um novo arquivo dockerfile utilizando as imagens alpine do dote core.
    Segue o link: https://github.com/Oragon/Oragon.Spring/pull/2

    Espero que esteja ok, pois é minha primeira contribuição com projetos open source.

    Responder
    • Luiz Carlos Faria

      Fabio, já recebi, estou entendendo como receber um PR. Para mim também é o primeiro recebido.

      Responder

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.

[docker 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.