Zona .Net

Refatorando idéias, produzindo conhecimento.

Archive for agosto 2009

Versionamento de Serviços WCF

leave a comment »

Uma das cosias que devemos ter em mente é que nossos aplicativos sempre irão sofrer alterações, seja por necessidade de evolução ou correção de bugs. Ter em mãos ferramentas que permitam que essas mudanças não sejam traumáticas é sempre bom e a gente só percebe a importância quando precisa.

Quando construímos um serviço WCF normalmente criamos um contrato fazendo uso de interfaces e a implementação desse contrato em uma classe. Além disso, devemos definir um Binding e um endereço do serviço, e tudo isso pode mudar. Caso eu tenha que adicionar ou remover uma nova operação (método), tenho que alterar o contrato. Posso querer alterar também o endereço onde o serviço será exposto, ou incluir um novo binding para contemplar interoperabilidade e por fim, posso mudar a implementação para refletir uma mudança de regra.

Algumas mudanças não afetam a forma como as mensagens são processadas, mantendo assim a compatibilidade com a versão anterior. Esse tipo de alteração é chamada de “nonbreaking change”, e as alterações que afetam o processamento da mensagem é chamada de “breaking change”.

O ideal é que todas as alterações sejam “nonbreaking change”, pois elas seriam transparentes para os consumidores do serviço. Lembrando que o contrato usado no server não precisa ser o mesmo usado no client, basta que sejam compatíveis em semântica. Então por padrão (ASP.NET Web Service e WCF) qualquer adição de operações no serviço não causará quebra de compatibilidade, já se houver remoção ou alteração de assinatura ou nome haverá quebra, esse comportamente é chamado de “Lax Versioning”, ou seja, não há validação de Schema. Vale ressaltar que nem toda plataforma ou tecnologia suporta Lax Versioning.

Caso haja necessidade de alterar um tipo que é passado entre uma chamada de uma operação, normalmente decorado com o atributo DataContract podemos tomar a decisão baseados no que queremos que os clientes vejam. Caso seja desejável que todos os cliente vejam o novo tipo alterado, devemos criar uma nova versão do DataContract, ServiceContract (em um namespace diferente) e um novo EndPoint, ou seja, o cliente terá que apontar para a nova versão do serviço. Se a modificação puder ser visualizada apenas para os novos clientes, é uma boa prática, embora não obrigatório que os DataContracts implementem a interface IExtensibleDataObject. Implementando essa interface embora os clientes antigos não vejam os novos membros, eles são passados entre as chamadas e não perdem seu valor, ou seja, são sempre serializados.

Vejamos alguns exemplos de alterações Nonbreaking.

Versao 01

<DataContract>
Public Class Cliente
	<DataMember>
	Dim Nome as String
End Class

Versao 02

<DataContract>
Public Class Cliente
	<DataMember(Name :=”Nome”)>
	Dim NomeCompleto as String
End Class

No caso acima o alteramos o nome do membro, mas não houve quebra, pois o DataContract não foi alterado, já que ele leva em consideração o parâmetro Name do atributo DataMember.

Agora vejamos um exemplo de inclusão de novo membro no DataContract.

Versao 01

<DataContract Name :=”Cliente”>
Public Class ClienteV1
	<DataMember>
	Dim Nome as String
End Class

Versao 02

<DataContract Name :=”Cliente”>
Public Class ClienteV2
	<DataMember>
	Dim Nome As String
  	<DataMember>
 	Dim CPF As String
End Class

Na versao 02 foi incluído um novo membro, mas a propriedade Name do atributo DataContract estão iguais, sendo assim são o mesmo contrato. Caso o cliente ainda esteja com a versão 01, ele pode receber o dado gerado no servidor usando a versão 02 (que serializará os dois membros), mas na deserialização ele não encontrara o membro CPF, logo irá ignorá-lo. Ao enviar o dado de volta para o servidor ele serializará apenas os membros que ele conhece, ou seja, apenas o membro Nome. Então quando ocorrer a deserialização no servidor será visto o membro enviado, ou seja, o servidor não verá mais o membro CPF que foi enviado, atribuindo assim o valor default. Com isso perdemos dados entre as chamadas, embora não haja quebra de compatibilidade entre os contratos.

Para evitar essa perda de informação entre as chamadas de versões diferentes usamos a interface citada anteriormente (IExtensibleDataObject). Isso deve ser feito logo na primeira versão, pois se implementada posteriormente as versões anteriores irão quebrar.

Versao 01

<DataContract>
Public Class Cliente
	Implements IExtensibleDataObject

	<DataMember>
	Dim Nome As String

    Public Overridable Property ExtensionData As ExtensionDataObject Implements _
	IExtensibleDataObject.ExtensionData
		Get
			Return oData
		End Get
		Set
			oData = Value
 		End Set
 	End Property
End Class

Versao 02

<DataContract>
Public Class Cliente
	Implements IExtensibleDataObject

	<DataMember>
	Dim Nome As String
	<DataMember>
	Dim CPF As String

    Public Overridable Property ExtensionData As ExtensionDataObject Implements _
	IExtensibleDataObject.ExtensionData
		Get
			Return oData
		End Get
		Set
			oData = Value
 		End Set
 	End Property
End Class

Dessa forma quando a infra-estrutura WCF encontra um dado que não faz parte do contrato original, ele armazena seu valor nessa propriedade. Quando o dado chega novamente ao remetente, que conhece a nova versão, ele extrairá o valor armazenado nessa propriedade e fará a deserialização sem perda de informação. Caso haja possibilidade de evolução do DataContract implemente essa interface desde a primeira versão, pois a implementação posterior gerará uma Breaking change.

Algumas alterações são sempre “Breaking changing” tais como trocar o Name ou Namespace do DataContract/ServiceContract, trocar a ordem dos membros do DataContract usando a propriedade Order do Atributo DataMember, renomear um DataMember/OperationContract, remover um DataMember/OperationContract, ou qualquer outra alteração de assinatura dos membros.

Apresentei apenas algumas idéias iniciais de como o WCF é robusto e flexível nessa questão de versionamento, para mais informações veja os links utilizados como referência.

Referências:
http://msdn.microsoft.com/en-us/library/ms733832.aspx
http://msdn.microsoft.com/en-us/library/ms731060.aspx
http://msdn.microsoft.com/en-us/library/ms731138.aspx
http://msdn.microsoft.com/en-us/library/ms733112.aspx
http://msdn.microsoft.com/en-us/library/ms731083.aspx


MVP Logo Leonardo Bruno Lima
Microsoft MVP | MCT | MCTS | MCPD
Attps Informática [http://www.attps.com.br]
+55 (85) 99449511
GMail: lblima.net@gmail.com
Live Mail: lblima_net@hotmail.com
   
Blogs: http://lblima.blogspot.com | https://zonadotnet.wordpress.com

“I do not agree with what you have to say, but I’ll defend to the death your
right to say it.” Voltaire.

Anúncios

Written by Leonardo Bruno Lima

25 de agosto de 2009 at 2:25 am

Publicado em Uncategorized

Tagged with ,

Qualidade de Código – Parte I

leave a comment »

Quem me conhece sabe que costumo escrever muito, especialmente quando o assunto é amplo e controverso. O convite para o blog surgiu no momento em que me aprofundo nesse intricado tema e pretendo, portanto, resumir nesta série de poucos posts, as experiências que tenho vivenciado nos últimos tempos.

Construir software de qualidade ainda é um desafio para as organizações. Seja pela premência do tempo, pela falta de maturidade ou mesmo pela simples falta de informação, muitas sequer inserem no seu processo de desenvolvimento, disciplinas que envolvam a aferição, testes e revisão do código fonte produzido.

Há um mito no estudo da qualidade que diz:

  Criar programas é uma arte que não pode seguir regras, normas ou padrões.

Removendo os absurdos, quantos de nós já não vivenciaram situações próximas a estas, especialmente às 23:00h do dia anterior ao prazo de entrega daquele projeto inadiável?

Não é meu objetivo aqui debater o extenso tema da qualidade, mas situá-la no âmbito das falhas inerentes ao elemento humano envolvido na elaboração de programas fontes[i] e nas alternativas automatizadas de aferição e testes recorrentes, com vistas a maximizar a produção de aplicações mais estáveis.

Quando se fala em aferir o código produzido, temos essencialmente três vertentes:

  1. Análise de métricas de código fonte (Code Metrics);
  2. Análise de conformidade a padrões de codificação (Static Rule Analysis);
  3. Análise manual do código fonte com base em listas de verificação (Checklists).

Destes métodos o meu preferido é de longe a análise de métricas. Digo isto porque as métricas de código fonte são baseadas em modelos matemáticos consagrados, em grande parte fruto de inferências da inspeção manual de código, amplamente pacificadas em ferramentas de mercado, algumas delas com décadas de existência.

Estes números são capazes de fornecer indícios importantes sobre a qualidade relativa do código fonte e, quando aplicadas durante o ciclo de desenvolvimento de um projeto, são capazes de revelar distorções antes vistas apenas nas etapas de testes integrados. 

Para exemplificar, vamos responder as seguintes perguntas:

  1. Ao analisar o código em uma classe o que lhe faria dizer se este ou aquele método é mais complexo?
  2. Qual critério você utilizaria para dizer que este ou aquele método está mais sujeito a falhar?
  3. Qual dos métodos é mais difícil de manter?

À minha mente vem imediatamente uma resposta para as três perguntas: “Ora, o método que possui mais linhas de código é o código mais complexo, mais propenso a falhas e mais difícil de manter.”

É nesta linha que nascem as métricas de código fonte. No exemplo acima a métrica é denominada SLOC (Source Lines Of Code).

Eu diria que na mesma proporção de complexidade dos sistemas atuais nasceram métricas e modelos matemáticos para atestar a exequibilidade, estabilidade, manutenibilidade e performance do código fonte gerado. Não vamos (e nem conseguiríamos) esgotar aqui todos estes elementos. Prefiro apresentar algumas métricas elementares, práticas e de grande valia.

Complexidade Máxima (CC): Originalmente denominada Cyclomatic Complexity, mede o nível de complexidade de um método/função. Elaborada por Thomas J. McCabe em 1976[ii] e aperfeiçoada por Steve McConnell da Microsoft em seu livro Code Complete de 1993.

A métrica CC mede o nível de complexidade através da contagem dos caminhos de execução distintos de um trecho de código. Cada método/função possui originalmente um CC igual a 1. Este valor é incrementado a cada instrução IF, ELSE, FOR, FOREACH, WHILE, TRY, CATCH encontrada. Estudos demonstram[iii] que trechos de código com CC superior a 25 possuem alto risco de conterem defeitos (>30%). Com CC superior a 60 a probabilidade cresce para 85% e a partir de 74 pontos, o risco vira fato.

Profundidade Máxima (NBD): Número máximo de blocos de código aninhados (Nested Block Depth) em um método ou função. Esta é outro forte indicativo da complexidade de um trecho de código. Estruturas de código IF, ELSE, FOR, FOREACH, WHILE aninhadas em excesso, além de tornarem a execução mais complexa, dificultam a compreensão e a legibilidade. Métodos/funções com NBD acima de 6 denotam necessidade de refatoramento.

Quantidade de Métodos Por Classe (MPC): Oferece um forte indicativo do grau de coesão de uma classe/library. Classes com muitos métodos apresentam uma forte probabilidade de acumularem mais responsabilidades que o necessário, violando o princípio da responsabilidade única (Single Responsiblity Principle).

Média de Instruções por Método (ASM): É uma derivação da métrica SLOC e mede a quantidade de instruções contidas nos métodos das classes, oferecendo claramente uma dimensão do seu tamanho.

Nível de Documentação do Código (PDOC): Mede a proporção de linhas que contém documentação (XML-DOC) em relação ao total de linhas de código num método, classe ou projeto. A proporção ideal situa-se entre 12% e 20%.

Nível de Comentários do Código (PCOM): Mede a proporção de comentários in-line em relação ao total de linhas de código num método, classe ou projeto. Idealmente, uma medida de 20% denota que o código está bem comentado. Códigos com proporção inferior a 5% e superior a 40% estão, respectivamente, pouco e muito comentados.

No próximo post, apresentarei ferramentas para coleta destas métricas e também exemplos de análise de código fonte. Até breve!


[i] Wohlin, C., Shull, F., Aurum, A., Ciolkowski, M., Petersson, M. (2002) “Software inspection benchmarking-a qualitative and quantitative comparative opportunity”. In: Software Metrics, 2002. Proceedings. Eighth IEEE Symposium, p. 118-127

[ii] McCabe (December 1976). “A Complexity Measure”. IEEE Transactions on Software Engineering: p. 308-320. http://classes.cecs.ucf.edu/eel6883/berrios/notes/Paper%204%20(Complexity%20Measure).pdf. 

[iii] Rich Sharpe. “McCabe Cyclomatic Complexity: the proof in the pudding”. Enerjy. http://www.enerjy.com/blog/?p=198.

Written by Gustavo Hurtado

18 de agosto de 2009 at 2:00 am

Publicado em .NET, Design Pattern

Repository Pattern

leave a comment »

O modelo de desenvolvimento DDD (Domain Driven Design) é hoje um dos mais prestigiados pela comunidade de desenvolvimento de software, sendo considerado, inclusive, uma maneira “mais OO” de se criar software, adicionando comportamento a objetos de domínio, provendo melhor testabilidade, manutenibilidade, etc. Além disso, um dos princípios do DDD é prover interfaces mais expressivas, com operações bem descritas pelos métodos que a executam.

No livro Domain Driven Design(a bíblia para muitos arquitetos e desenvolvedores), do Eric Evans, o pattern Repository integra a camada de domínio e é usado como uma ferramenta para selecionar objetos de domínio de acordo com um determinado critério.

Pela definição do Fowler, no livro PoEAA(Patterns of Enterprise Application Architecture), o Repository tem o seguinte papel:

Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.

Traduzindo, o Repository tem o papel de mediador entre o domínio e as camadas de mapeamento de dados para permitir o acesso a objetos de domínio.

Ele complementa dizendo:

A system with a complex domain model often benefits from a layer, such as the one provided by Data Mapper, that isolates domain objects from details of the database access code. In such systems it can be worthwhile to build another layer of abstraction over the mapping layer where query construction code is concentrated. This becomes more important when there are a large number of domain classes or heavy querying. In these cases particularly, adding this layer helps minimize duplicate query logic.

Ou seja, a idéia é, no domínio,  abstrair o Data Mapper – responsável pela persistência dos dados, através do Repository, provendo uma linguagem declarativa para construção de queries.

Na prática, os frameworks ORM como NHibernate, Linq To Sql e Entity Framework, fazem o papel do Data Mapper e são eles que devem/podem ser abstraídos.

Na minha opinião, a grande sacada do Repository é fornecer uma linguagem declarativa para construção de queries, pois é ela quem dá ao domínio a liberdade de fazer a consulta que necessitar, sem alterar uma única linha de código do seu repositório. E é isso o que a maioria tem “esquecido” de incluir nas suas implementações.

Já vi implementações que, sob minha ótica, não tiram proveito do pattern, fazendo dele apenas um proxy para o seu respectivo DAO(ou outro pattern para acesso a dados), sob o argumento de que o Repository é uma forma mais expressiva por ter métodos do tipo repository.listarClientesVIP() encapsulando uma chamada a um método do DAO, como dao.consultar(StatusCliente.VIP).  Esse tipo de expressividade até um DAO pode ter e não acho que essa seja a idéia.

Se você observou bem, viu que deixei em aberto a possibilidade de abstração do framework ORM. Isso por que, como em todas as escolhas que fazemos, a escolha de um pattern tem seus tradeoffs. E, apesar de trazer muitos ganhos na abstração da infraestrutura de persistência de dados, a implementação da linguagem declarativa de query é muito custosa, pois requer a implementação de várias operações para composição do critério. Particularmente, faria essa implementação se percebesse a possibilidade de, no meio do projeto, precisar trocar o framework de persistência usado na aplicação. Caso contrário, usaria os mecanismos de construção de queries fornecidos pelo ORM, para assim tirar o máximo de proveito do framework.

Written by Hamon Vitorino

11 de agosto de 2009 at 4:52 am

Publicado em .NET, Design Pattern

Tagged with , ,

Streaming com WCF

leave a comment »

Em alguns serviços existe a necessidade de tráfego de grande volume de dados onde é inviável esperar o recebimento total da mensagem para então ser processada. Por isso no WCF temos a possibilidade de trafegar dados em forma de stream, ou seja, sem a necessidade de aguardar o carregamento completo do objeto na memória para então enviá-lo. Dessa forma o receptor já começa a processar a mensagem antes do termino do envio, aumentando assim a escalabilidade.

Vale ressaltar que essa forma de transferência não esta disponível para todos os bindings, apenas para os seguintes: BasicHttpBinding, NetTcpBinding e NetNamedPipeBinding.

Para implementar tal funcionalidade, lançamos mão da propriedade TransferMode que recebe uma das opções do enumerador de mesmo nome. São elas:

Buffered: A mensagem é carregada totalmente na memória antes de ser enviada ao destinatário. É a configuração padrão para todos os bindings.

Streamed: A mensagem e enviada como um stream, exceto o header que é carregado completamente, não excedendo o MaxBufferSize.

StreamedRequest: Apenas a requisição será enviada como stream, sendo  a resposta coloca em buffer antes do envio.

StreamedResponse: É o inverso do anterior, apenas a resposta será enviada em forma de stream.

Existem algumas regras para uso de stream com WCF, uma delas é que a operações/métodos que compõe o contrato recebam ou retornem uma instancia da classe stream ou uma implementação da interface IXmlSerializable.

Um detalhe a ser observado é que o Header da mensagem será sempre posto em buffer.

Tenha em mente também que ao utilizar stream para ganhar performance você abre mão de alguns recursos como transações, segurança no nível de mensagem, etc.

Veja o contrato de serviço abaixo:

[ServiceContract]
public interface IArquivos
{
   [OperationContract]
   Stream Download(string nomeArquivo);

   [OperationContract]
   bool Upload(Stream arquivo);
}

As operações Upload e Download recebem e devolvem streams respectivamente. Veremos agora a implementação desse contrato:

public class FileService :   IArquivos
{
   public Stream Download(string   nomeArquivo)
   {
      return new FileStream(@"..\Files\"   + nomeArquivo, FileMode.Open);
   }

   public bool Upload(Stream arquivo)
   {
      string nomeArquivo =  "BinFile.bin";
      using (FileStream fs = new   FileStream(@"..\Files\" + nomeArquivo, FileMode.Create))
      {
         byte[] buffer = new   byte[8192];
         int bytesRead = arquivo.Read(buffer,   0, 8192);

         while (bytesRead > 0)
         {
            fs.Write(buffer, 0,   bytesRead);
            bytesRead =   imagem.Read(buffer, 0, 8192);
         }
      }
      return true;
   }
}

Outro detalhe importante é em relação ao controle de fechamento do stream, assumimos que sempre quem recebe deve fechá-lo, já que não sabemos quando o receptor realmente recebeu e processou toda a mensagem. No exemplo acima estamos utilizando o bloco using, que invocará o método dispose do objeto FileStream ao sair do escopo.

Vejamos agora o Host desse serviço:

Em uma aplicação console application, adicione a referencia do projeto do serviço e colocamos o seguinte código no método Main:

using (ServiceHost host =   new ServiceHost(typeof(FileService), new Uri[] { new   Uri("http://localhost:7744") }))
{
   BasicHttpBinding objHttpBinding =   new BasicHttpBinding();
   objHttpBinding.TransferMode =   TransferMode.Streamed;
   objHttpBinding.MaxBufferSize =   3065536;
   objHttpBinding.MaxReceivedMessageSize   = 3065536;

   host.Description.Behaviors.Add(new   ServiceMetadataBehavior() { HttpGetEnabled = true });
   host.AddServiceEndpoint(typeof(IArquivos), objHttpBinding, "arquivos");
   host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(),   "mex");

   host.Open();
   
   Console.ReadLine();
}

No host do serviço eu alterei as propriedades MaxBufferSize e MaxReceivedMessageSize, onde a primeira afeta apenas o que é trafegado no header, que é a única parte da mensagem que utiliza buffer.

Vimos que a implementação de mensagens via stream é bem simples e se usada com cautela pode aumentar a escalabiliade de um serviço WCF, também vimos como o WCF é flexível e consegue um baixo acoplamento entre a implementação do serviço e da sua execução por parte do host.


MVP Logo Leonardo Bruno Lima
Microsoft MVP | MCT | MCTS | MCPD
Attps Informática [http://www.attps.com.br]
+55 (85) 99449511
GMail: lblima.net@gmail.com
Live Mail: lblima_net@hotmail.com
   
Blogs: http://lblima.blogspot.com | https://zonadotnet.wordpress.com

“I do not agree with what you have to say, but I’ll defend to the death your
right to say it.” Voltaire.

Written by Leonardo Bruno Lima

4 de agosto de 2009 at 3:23 am

Publicado em .NET, Streaming, WCF

Tagged with , ,