.NET Cross PlatformDesenvolvimentoPatterns & Practices

Falar de WCF em meados de 2017 parece algo sem sentido, no entanto considero relevante tornar públicas soluções e alternativas que muitas vezes só apresentava dentro dos times por onde passei. São soluções, ideias, conceitos que ajudam no desenvolvimento e tornam o dia-a-dia de desenvolvimento mais fácil para quem está ciente dos conceitos ao redor da plataforma. Ignorei a possibilidade de publicar esse tipo de conteúdo para evitar exposição, já que obviamente, os preguiçosos irão reclamar, e muito! De qualquer forma asseguro, se você usa WCF e nunca viu esses conceitos, acredito que ao concluir esse post verá a tecnologia como algo menos místico, e até poderá criar alguma empatia por ela. É ambiciosa a minha proposta, eu sei!


Contextualizando

WCF

WCF ou Windows Communication Foundation é uma tecnologia entregue pela Microsoft nos updates do .NET Framework ainda sob a CLR 2.0, mais especificamente no .NET Framework 3.51. Tem o papel de unificar o modelo de hospedagem, gestão, configuração e consumo de serviços na plataforma, oferecendo um modelo consistente e gerenciável para os mais diversos cenários, exigindo pequenas mudanças em seu código, em troca exigindo extensa configuração. Na prática, construa seus serviços como classes simples, reaproveitáveis, levando em conta poucas regras e um conjunto de configurações será capaz de criar tudo o que você precisa para hospedar e consumir serviços, nos mais variados protocolos, envolvendo segurança, gestão e flexibilidade ao processo de hospedagem e consumo de serviços.

Regras

As premissas para a implementação de um serviço WCF são bem fáceis de se cumprir: Serviços precisam de Interfaces, estas interfaces precisam de alguns atributos/anotations/decorators específicos. Quanto à troca de dados, use value types ou tipos complexos, marcados, novamente com alguns atributos. Essas Interfaces e Transfer Objects são os contratos dos serviços que você expõem.

Integração com Visual Studio

A hospedagem de serviços WCF por padrão segue um tipo de projeto diferenciado no Visual Studio, um tipo de projeto bem parecido com um projeto web, só que dedicado a hospedagem de serviços. Ao mesmo tempo, para referenciar um serviço, basta adicionar um tipo de referência um pouco diferente: O Service Reference. Que se baseia em um wizard no qual você informa o endereço de um endpoint WCF e o visual studio se encarrega de gerar um proxy para você, com classes, e todo o metadado necessário para chamar o serviço de forma fortemente tipada. Embora no Visual Studio tenhamos um projeto diferenciado, este só tem o papel de se integrar ao IIS. Na minha experiência pessoal mais atrapalha do que contribui, pois cria uma visão torpe de que essa é a única forma de construir serviços WCF. Pra piorar, cria contratos e serviços em um mesmo projeto.

Deploy

Embora existam outras formas, as mais comuns estão associadas ao IIS, com os famosos arquivos SVC.

Por incrível que pareça, os arquivos SVC’s só tem uma única linha, como apresentada acima. A utilização de arquivos SVC tem relação direta com os handlers do asp.net e forma de configuração/injeção no IIS, pois estes arquivos fazem a ativação dos serviços, auxiliando no endereçamento, inclusive. Mas nem de longe essa é a única forma de publicar serviços WCF, cada um tem a sua, e a minha é utilizando serviços Windows. A propósito, embora alguns protocolos possíveis no WCF sejam interoperáveis e suporte cross platform, a tecnologia em si não é. Hoje você apenas consegue consumir serviços WCF do .NET Core, por exemplo.


Show Time

Vamos às principais questões:

  1. Um serviço WCF pode ser criado em qualquer tipo de projeto. Class Library, Console, Windows Forms. Qualquer projeto baseado em Full Framework do qual você possa construir uma classe e uma interface lhe permite criar um serviço WCF.
  2. O mesmo vale para hospedagem e consumo.
  3. Se você tiver a interface do serviço, não precisa de um proxy programático daqueles gerados pelo Visual Studio.

Ok, mas o que há de errado com as Service References?

Service References funcionam bem, mas dependem de um serviço publicado e um endpoint real para que você possa referenciar, além disso, a cada mudança no serviço, todo consumidor precisa atualizar a referência no Wizard que citei na primeira sessão do texto. E isso não é de todo ruim, é a melhor alternativa quando estamos falando com serviços expostos por outras empresas que não fornecem outra forma de entregar os devidos contratos de seus serviços. No entanto quando estamos usando em uma mesma empresa, com um conjunto de projetos de uma mesma solution ou de solutions diferentes, atualizar service reference passa a ser algo um tanto quando burocrático demais, a ponto de limitar mudanças no contrato para evitar o dano em seus consumidores. Não estou advogando a favor das mudanças de contrato, mas sejamos conscientes: Elas existem, e quanto mais indolor for qualquer mudança, melhor para todos.

Qual a alternativa?

Isolar e compartilhar assemblies de contratos. Isso quer dizer que ao invés de criar interfaces e implementações em um único assembly (ou projeto), você deve isolar interfaces/TO’s em um assembly, enquanto implementação segue em outro. Esse tipo de abordagem te lembra alguma coisa? Sim, é um pattern conhecido! Diversos projetos que possuem um assembly com prefixo ou sufixo Common ou Contracts implementam dessa forma suas abstrações, permitindo que você escolha qual implementação é mais adequada para cada cenário de uso. No nosso caso, a variação é que a implementação pode estar local ou remota, e a decisão não fica proliferada no coração da solução, sua infra de injeção de dependência pode ser responsável por isso, tornando o desenho limpo e fluido.

Um ponto a ressaltar é que quando digo compartilhar assembly, me refiro a compartilhar projetos em uma mesma solução e compartilhar pacotes nuget quanto estamos falando de projetos em outras soluções ou até empresas. Esse tipo de gestão favorece inclusive o desenvolvimento, visto que você pode definir um contrato de serviço muito antes deste estar publicado em qualquer servidor e ainda assim conseguir compartilhar com todos os envolvidos via MyGet, NuGet ou um host nuget privado.

Projeto de Exemplo

Criei um projeto de exemplo no GitHub para demonstrar o que estou propondo: https://github.com/luizcarlosfaria/dotnet-rethink-wcf . Esse projeto conta com 4 projetos:

  • WcfClientConsole
  • WcfHostConsole
  • MyServiceApp.ServiceContracts
  • MyServiceApp.ServicesImplementations

Os dois primeiros projetos fazem host e consumo do serviço WCF, ao passo que o terceiro projeto possui os contratos do serviço, enquanto o último sua implementação. Tudo o que falamos arqui é abordado nesse projeto de exemplo inclusive os temas abaixo.

O projeto WcfClientConsole depende exclusivamente dos contratos (MyServiceApp.ServiceContracts), e usa Build Events para copiar a implementação para a pasta de execução do exe. Isso para evitar uma referência, muitas vezes usada exclusivamente para gerar a cópia dos binários de um lado para o outro.

Namespaces

Muitas coisas interessantes podem ser tiradas desses projeto, como por exemplo, como compartilhar namespaces, algo pouco utilizado, mas muito útil. Esse aspecto do projeto garante uma excelente semântica na organização de namespaces, facilitando quem consome seus projetos. Afinal, pare de usar pastas/namespaces “Interfaces” nos projetos, é feio!

Build Events

Outra feature muito útil são os Build Events, que permitem criar ações para Pre-Build e Post-Build2. Excelente quando você não quer que algo seja referenciado diretamente por um projeto, mas precisa estar lá para o runtime. Seja a cópia do FFMPEG ou um projeto que implemente algo que você não quer ver referenciado, o Post-Build te ajudará na resolução desse problema. Sua função é executar comandos antes e depois do build de um projeto e pode ser usado com qualquer finalidade.

Voltando ao projeto

Esses pontos acima foram abordados para exemplificar como são criados projetos no mundo real, fora do Hello World, ou mesmo Hello Build! Mas nosso foco ainda é o WCF, portanto vou me ater nesse aspecto para montar as considerações finais.

Nesse modelo uma pequena mudança no contrato do serviço causa uma quebra na aplicação ainda durante o build, possibilitando assim perceber que algo está errado muito antes dos testes integrados.

Outro aspecto relevante é que com essa estratégia, é possível executar serviços locais e remotos sem que os consumidores tomem ciência de onde está a implementação. Esse é o real poder da indireção e da inversão de controle, que somada a injeção de dependência pode lhe trazer sistemas completamente configuráveis e escaláveis ao com a simples troca de uma configuração. Perceba que não há referência para a implementação real no projeto WcfClientConsole. Há um motivo muito bom para isso: É preferível usar o build event para fazer a cópia do assembly de implementação, do que simplesmente adicionar a referência para a implementação, pois o resultado pode ser desastroso. Basta uma instanciação direta e equivocada da implementação do serviço no lugar errado para comprometer sua arquitetura.

 

Nos projetos Web, onde temos a transformação dos arquivos Web.Debug.config e Web.Release.config, trocar configurações fica ainda mais fácil, podendo trabalhar com endpoint interno e endpoint externo, de acordo com a estratégia usada.

Como as quebras por incompatibilidade nos contratos acontecem durante o build, ganhamos maior agilidade na identificação e correção, além de refactoring extremamente eficiente quando falamos de serviços e consumidores em uma mesma solução.

Como dito na documentação do projeto de exemplo no GitHub, este tem função de exemplificar algumas características de projetos reais de produção usando features esquecidas ou até mesmo desconhecidas.

Conclusão

Bom, são diversos os ganhos obtidos com essas estratégias, e você, o que achou? Ainda vai continuar usando Service Reference? A propósito, sobre os pontos negativos: A falta de conhecimento sob as features da plataforma. Sob como funcionam as referências, assembly load, possibilidade de hospedar um serviço WCF fora do IIS, e coisas assim são inconvenientes, no entanto é importante ressaltar que não é um gap da arquitetura, é um gap do profissional, que mesmo sendo desenvolvedor, opta por ser usuário da tecnologia que escolheu para trabalhar todo dia.

Não deixe de baixar e rodar o projeto que coloquei no GitHub, nele você poderá ver o exemplo, real.

Esse foi um exemplo sem injeção de dependência, baseado no .NET Full Framework 4.6.2, mas poderia ser um exemplo com IoC e DI. Talvez mereça apresentar esse mesmo modelo desenvolvido com Spring.NET para demonstrar real injeção de dependência e inversão de controle, quem sabe? Pede aí!

Observações

1 | .NET 3.0 e 3.5 não são versões da CLR, são versões do .NET Framework, após o 2.0 a nova versão da CLR é a 4.0 (saiba mais)

2 | No exemplo acima uso XCOPY que é um utilitário presente no windows, desde seu nascimento (na verdade nasceu com o DOS 3.2). Sobre COPY e XCOPY: Se for copiar arquivos, use XCOPY, se for copiar pastas use COPY. Tem a ver com a facilidade de uso, escolha uma escolha pessoal.

Comente, compartilhe, curta!

Logo abaixo desse texto você encontra os Posts Relacionados, e botões de compartilhamento, em seguida a sessão de comentários!

Gostou? Então aproveite para curtir, compartilhar e enviar comentários, dúvidas ou sugestões.

Conheça o Grupo Arquitetura de Softwate | .NET: Facebook e Telegram
Luiz Carlos Faria: Site, Youtube, Facebook, Twitter, Telegram, Linkedin e Email