terça-feira, 14 de outubro de 2008

Poster C#

Segue no link abaixo um pdf que contém todos os keybindings do C# do Visual Studio 2008.

Muito bom! Vale a pena dar uma lida!

http://www.microsoft.com/downloads/details.aspx?familyid=E5F902A8-5BB5-4CC6-907E-472809749973&displaylang=en

sábado, 4 de outubro de 2008

Princípio da Segregação de Interface

O princípio da segregação de interfaces ajuda a resolver o problema das interfaces “gordas”, que são classes cuja interface não possui coesão com o modelo tornado as interfaces poluídas e disponibilizando métodos desnecessariamente ao cliente.

Os clientes não devem ser forçados a depender de métodos que eles não utilizam, ou seja, quando um cliente 1 depende de uma classe que contém métodos que o mesmo não utiliza, porém o cliente 2 os utiliza, o cliente 1 será afetado pelas mudanças que o cliente 2 necessite fazer na classe. Para evitar este tipo de acoplamento é que devemos separar as interfaces.

Como exemplo, considere as interfaces de um terminal de auto atendimento bancário. A interface do usuário deve ser bem flexível, possibilitando que a saída seja feita de diversas formas como na tela do equipamento ou falada por um sintetizador de voz. Para esta parte, a flexibilidade pode ser obtida através de um classe abstrata que possui todos os métodos necessários para que os dados sejam apresentados pela interface.

Consideremos também que cada transação efetuada no terminal é derivada de uma classe chamada “Transação”, logo possuímos as classes “Deposito”, “Saque” e “Transferencia”. Cada classe deve utilizar os métodos da interface com o usuário. 

A princípio possuímos um modelo como demonstra a ilustração 1.

Ilustração 1 – Hierarquia da Transação

Ao analisar melhor o modelo da ilustração 1 observamos que cada transação está sendo forçada a depender de métodos não interessantes às mesmas. Nesta situação, se alguma transação for modificada, todas as outras transações serão afetadas além das classes que dependem da interface IU. O pior acontecerá ao tentar reutilizar estas classes, pois é necessário levar junto muita coisa que não interessa. Tudo isto exala odores como rigidez, fragilidade e viscosidade.

Esses problemas observados podem ser evitados segregando a interface IU em partes individuais como “IUDeposito”, “IUSaque” e “IUTransferencia” como demonstra a ilustração 2.


Ilustração 2 – Interface IU segregada

Portanto os clientes devem depender apenas dos métodos que eles irão utilizar. Este objetivo pode ser alcançado quebrando a interface da classe poluída em interfaces específicas. Com isso é quebrado a dependência dos clientes de métodos que eles não utilizam criando uma independência mútua entre os clientes.

Princípio da inversão de dependências

Os métodos de desenvolvimento de software mais tradicionais tendem a criar estruturas onde módulos de alto nível dependem de módulos de baixo nível e onde política de negócios depende de detalhes.

Este tipo de dependência é prejudicial pois geralmente módulos de alto nível contém o modelo de negócios da aplicação. Se módulos como estes dependerem de módulos de baixo nível, mudanças efetuadas nos módulos de baixo nível podem afetar diretamente os módulos de alto nível forçando estes a mudar.

Os módulos de alto nível devem ser isolados de módulos com menor importância no modelo de negócios. Sem o isolamento, torna-se impossível o reuso dos modelos de negócios implementados nos módulos de alto nível.

Segundo Booch, “todas as arquiteturas orientadas a objetos bem estruturadas possuem camadas claramente definidas, sendo que cada camada disponibiliza um pacote coerente de serviços através de uma interface definida.”. Podemos utilizar como exemplo a estrutura representada na ilustração 1:

Ilustração 1 – Exemplo de um modelo em camadas

Apesar de aparentar correto, o modelo demonstra que a camada de política é sensível a mudanças em todas as camadas inferiores uma vez que dependência é transitiva, ou seja, a camada de política depende da camada de controle e também da camada de utilidades.

Para aplicar o principio da inversão de dependências, devemos ter como base as seguintes teorias:
• Módulos de alto nível não devem depender em módulos de baixo nível. Ambos devem depender de abstrações.
• Abstrações não devem depender de detalhes. Detalhes devem depender de abstrações. 

A ilustração 2 demonstra um modelo mais apropriado. Cada camada superior declara uma interface abstrata para o serviço que ela necessita. Cada classe de alto nível utilizam as classes de baixo nível através de uma interface abstrata.


Ilustração 2 – Camadas invertidas

Na ilustração 2 pode-se observar que as camadas superiores não dependem das camadas inferiores. Ao invés disso, as camadas inferiores dependem das interfaces de serviços abstratos declarados nas camadas superiores. Com a aplicação do principio podemos observar que não a dependência transitiva entre as camadas foi quebrada, resolvendo os problemas citados anteriormente. 

Princípios da Substituição de Liskov

O princípio de substituição de Liskov, criado por Bárbara Liskov, determina que funções que utilizam referências a classes base devem estar aptas a utilizar objetos de classes derivadas sem nem ao menos conhecê-los.

A importância deste princípio é obvia quando são consideradas as conseqüências de violá-lo. Por exemplo, seja “F” uma função que recebe como parâmetro uma classe base “B”. Presumindo que ao passar um objeto “D”, de uma classe derivada de “B”, cause um comportamento errado em “F”. Logo, “D” viola o princípio de substituição de Liskov e o mesmo é considerado frágil quando utilizado por “F”. Para testar esta função, deverá ser criado um teste particular para “F” ao receber “D” como parâmetro. Este teste viola o princípio aberto/fechado porque “F” não é fechado a extensões de “B”.

Como exemplo de violação do princípio, considere a aplicação que utiliza um retângulo. A princípio essa aplicação funciona corretamente, porém o cliente decide que a aplicação deve também trabalhar com um quadrado. Um quadrado não é nada mais que um retângulo com lados iguais, logo a classe quadrado deve derivar de retângulo. Costuma-se interpretar esse tipo de relacionamento de herança entre duas classes como “é um” ou “é um tipo de”, neste caso, quadrado é um tipo de retângulo como mostra a ilustração 1.

Ilustração 1 – Quadrado herda de Retângulo

Este tipo de análise de relacionamento é visto algumas vezes como uma das técnicas fundamentais da análise da orientação a objetos. Porém este tipo de análise pode induzir a erros significantes que apenas serão notados na implementação.

Uma falha que pode ser apontada no exemplo é o fato de que um quadrado não necessita ter as propriedades Base e Altura, uma vez que todos os lados do quadrado são iguais. Estas propriedades herdadas não são apropriadas quando o objeto em questão é um quadrado, porém é possível contornar a situação sobrescrevendo os métodos “get” e “set” da Base e da Altura para que quando alguém atribuir um valor à base, o método atribui o mesmo valor à altura e vice-versa.

Agora considere uma função onde é passado como parâmetro um retângulo:

Função fx(Retângulo r)
{
    r.base = 5;
    r.altura = 4;
    se (r.Area() != 20)
        //Exibe Erro: Área incorreta;
}

A função utiliza os atributos base e altura de um suposto retângulo. A função funciona corretamente quando lhe é passado um retângulo mas dá errada se um quadrado for passado. A função “fx” é frágil se levarmos em consideração a hierarquia quadrado/retângulo. A função citada viola o principio de substituição de Liskov já que é possível existir uma função que receba um retângulo como parâmetro e não funcione corretamente se for passado um quadrado, por este motivo o quadrado não é substituível por um retângulo, ou seja, sua classe base.


quarta-feira, 1 de outubro de 2008

Princípio Aberto / Fechado (Open/Closed Principle - OCP)

Ao desenvolver um sistema, deve-se ter em mente que o sistema sofrerá várias mudanças no decorrer do seu ciclo de vida. Para ter um design suscetível a novas funcionalidades, podemos aplicar o princípio aberto/fechado definido por Bertrand Meyer que diz que as entidades do sistema (classes, módulos, funções, etc.) devem ser abertas a extensões, mas fechadas a modificações.

Aberto a extensões, pois o módulo deve estar preparado para suportar novos comportamentos e atender novos requisitos. Fechado a modificações, pois mesmo estendendo o módulo com novos comportamentos, o mesmo deve permanecer intacto, sem mudanças no código fonte. 

Este princípio atende diretamente o odor de rigidez de um software devido ao fato de que o mesmo torna-se maleável para que sejam implementadas novas funcionalidades apenas adicionando novas linhas de código sem ter que modificar as linhas de código já testadas.             

Para aplicar este princípio no design de um sistema, é necessário o uso de abstrações. As abstrações são classes-base que representam possíveis comportamentos cujas classes derivadas deverão possuir. 

Se um módulo é capaz de manipular uma abstração, o mesmo pode então ser fechado a modificações, desde que ele dependa apenas da abstração, e aberto a extensões criadas a partir de novas derivações da abstração. 

O exemplo a seguir ilustra o princípio aberto/fechado a partir de uma aplicação simples que tem por objetivo apenas desenhar formas geométricas na tela do usuário.   


Ilustração 1 – Sistema rígido  

A ilustração 1 mostra o design do sistema citado sem se importar com o princípio aberto/fechado. Observe que é um design rígido uma vez que o módulo de desenhar as formas está dependendo diretamente do tipo da forma que deve ser desenhada. Se futuramente tiver que ser adicionado outra forma, será necessário alterar o módulo para o mesmo atender esse novo requisito.   

Ilustração 2 – Sistema conforme o princípio aberto/fechado

A ilustração 2 mostra o mesmo sistema, porém aplicando o principio aberto/fechado. Note que se for necessário estender o comportamento da função “DesenharTodasFormas”, da classe “DesenharFormas”, para atender um novo tipo de forma, será necessário apenas criar uma nova classe derivada da classe “Forma”. Desse modo a classe “DesenharFormas” não sofrerá nenhuma mudança, logo o comportamento do módulo pode ser estendido sem sofrer mudanças.             

No novo design modelado, podemos identificar algumas características. O sistema não é frágil, pois mesmo depois de adicionar um novo comportamento a ele, é desnecessária a revisão do código para achar partes que foram afetadas pela alteração.             

O sistema também não é rígido já que para adicionar uma nova forma para ser desenhada é bem simples, sendo necessário apenas criar uma nova derivação e implementar suas funções.             

Por último, o sistema não é imóvel. A classe “DesenharFormas” pode ser reusada por qualquer outra aplicação sem a necessidade de levar as classes “Quadrado” e “Circulo” com ela. Portanto o novo design não é caracterizado por nenhuma das características de um mau design mencionadas.             

Concluindo, o principio aberto/fechado é um dos principais princípios do design orientado a objetos porque com ele atingimos os melhores benefícios da orientação a objetos: flexibilidade, reusabilidade e manutenibilidade.

Nomeando Classes, Estruturas, Interfaces, Métodos e Membros.

- Uma regra geral válida para esses identificadores é utilizar nomes de substantivos. Se não for possível utilizar esta regra, tem algo de errado e que precisa ser reformulado.  

- Utilize nomes simples e fáceis de serem compreendidos em tipos que serão utilizados com mais freqüência.   

- Se for possível, termine o nome do identificador com o nome de sua classe pai. Ex. ArgumentOutOfRangeException, é um tipo de Exception.   

- Interfaces devem possuir a letra “I” como prefixo. Ex. IComponent.   

- Para uma classe que é uma implementação padrão de uma interface, diferencie o nome da classe apenas retirando o prefixo “I”. Ex. class Component : IComponent   

- Para enumerações (Enums) não utilize o sufixo “Enum”. Ex. enum ColorEnum {...}   

- Para métodos, utilize nomes de verbos ou frases com verbos.   

- Uma propriedade deve ser nomeada com um substantivo ou um adjetivo.   

- Não utilize “Get” para propriedades.   

- Para propriedades cujo retorno é booleano, utilize afirmações no nome. Ex. EstáAtivo ao invés de NãoEstáAtivo.     

Aqui termina esta parte de “Naming Guidelines”. Como já foi dito, o nome dado aos seus identificadores pode fazer muita diferença e ajuda a evitar muitas dores de cabeça, além de deixar o código mais limpo e legível.

Obrigado!
Denis Yomura

terça-feira, 30 de setembro de 2008

Regras de nomeação de Namespaces

Namespaces são principalmente utilizadas para organizar suas implementações em uma hierarquia lógica e fácil de ser utilizada. Abaixo algumas diretrizes para dar nome às Namespaces:   

- Como os nomes dos identificadores, o nome da Namespace deve ser facilmente interpretado, ou seja, deve ser possível saber suas funcionalidades através do seu nome.   

- Se precisar de algum padrão, considere o seguinte:               {Empresa}.({Produto}|{Tecnologia}) [.{Características}] [.{Subnamespace}] – Ex. Microsoft.VisualStudio.Design   

- É uma boa prática sempre colocar o nome da empresa como prefixo para evitar confusões caso haja uma Namespace com o mesmo nome de outra empresa, por exemplo.   

- No segundo nível da Namespace, escolha algo imutável, ou seja, que não irá mudar a cada versão lançada. Lembre-se que a Namespace será algo que ficará fixo no código, sendo algo muito trabalhoso para ficar mudando a cada release do seu produto.   

- Não utilize nomes “virtuais” em suas Namespaces, por exemplo, o nome dado para a equipe que você está trabalhando em um determinado projeto. Esses nomes tendem a mudar dependendo da estrutura organizacional da empresa.   

- Sempre utilize o PascalCasing e separe os componente das namespaces utilizando o ponto ”.”.   

- Se for necessário e fizer sentido, pluralize. Essa regra não deve ser utilizada no caso de siglas. Ex. System.Collections e System.IO.   

- Não utilize nomes genéricos demais como Element, Node, Log e Message. A probabilidade de haver nomes iguais é muito grande. Utilize então FormElement, XmlNode, EventLog e SoapMessage.

Regras de nomeação de Assemblies / Dlls

- Na escolha do nome físico de seus arquivos “.dll”, tente escolher algo bem abrangente, que descreva o propósito de suas funcionalidades. Ex. System.Data    

- Se precisar de algum padrão, considere o seguinte:

[Empresa].[Componente].dll – onde [componente]  pode ser conter mais de um nome. Ex. Microsoft.VisualBasic.Vsa.dll    

- Também faz sentido nomear o arquivo seguindo o nome da Namespace nele implementada.

Referencias:

segunda-feira, 29 de setembro de 2008

Princípio da Divisão de Responsabilidades (Single-Responsibility Principle)

O princípio da divisão de responsabilidades, também conhecido como coesão, defende que uma classe deve possuir apenas um, e apenas um, motivo para ser modificada. Se a classe possuir mais de um motivo para ser modificada então ela está mal definida.

A classe somente terá mais de um motivo para ser modificada se a mesma for responsável pelas ações às quais ela foi definida e por outras fora de seu escopo. No contexto do princípio da divisão de responsabilidades, responsabilidade é definida como razão para mudanças. Se for possível imaginar mais de um motivo para modificar a classe, logo esta classe possui mais de uma responsabilidade.

No exemplo a seguir foi definida uma interface “Modem” onde as funções declaradas parecem corretas dentro do contexto das funcionalidades que um modem deve exercer.


Ilustração 1 - Interface Modem

Porém, analisando detalhadamente a iliustração 1 é possível identificar que a classe possui duas responsabilidades: conexão e comunicação. As funções “Dial” e “Hangup” controlam a conexão e as funções “Send” e “Recv” controlam a comunicação.

Se os requisitos da aplicação mudassem de modo a afetar mais frequentemente as funções de conexão, o design demonstraria rigidez, um dos odores de decomposição do software. A aplicação seria rígida porque ao alterar algo na conexão seria necessário compilar e distribuir novamente toda a parte de comunicação sem necessidade alguma.
A ilustração 2 demonstra como ficaria o design ao separar as responsabilidades.


Ilustração 2 - Interface modem separada

Para separar as responsabilidades da interface modem de modo que a aplicação não sofra de rigidez, foi necessário aumentar a complexidade do sistema. Todavia deve-se tomar muito cuidado ao aplicar o princípio, pois se os requisitos da aplicação fossem tais que sempre alterariam ambas as responsabilidades, conexão e comunicação, o novo design seria incorreto devido ao fato de ter gerado complexidade desnecessária à aplicação, outro odor da decomposição do software. Portanto a aplicação do princípio de divisão de responsabilidades, ou outro qualquer, deve ser feito de modo cuidadoso e inteligente. Só se deve utilizá-los se o design estiver exalando algum dos odores de decomposição do software.



Design Ágil

Design ágil é um processo continuo de princípios, modelos, e praticas visando a melhoria da arquitetura do software.

Design de uma aplicação não são apenas diagramas UML. Os diagramas UML podem representar partes do design, mas ainda não é o design propriamente dito. O design de um software é um conceito abstrato que representa de uma forma macro a estrutura do sistema como um todo e ao mesmo tempo de forma detalhada cada módulo, classe e método.

O design pode ser representado por diversas maneiras, mas no fim das contas, o código fonte é a principal fonte de design. Um design mal feito, consequentemente um código mal desenvolvido, pode acelerar o processo de decomposição do software, sendo que a velocidade de decomposição de um software está diretamente ligada à sua qualidade, ou seja, quanto maior a velocidade de decomposição do software, menor é a qualidade. Para evitar tal decomposição, deve-se ficar atento aos sintomas ou odores que o software exala.

Desenvolvedores ágeis se dedicam para manter o design coeso, limpo, e livre de sintomas ou odores sempre que possível. Eles sabem como fazer isso porque sempre seguem alguns passos antes de escrever qualquer linha de código:

1. Eles acham o problema seguindo praticas ágeis;
2. Eles analisam o problema aplicando princípios de design;
3. Eles resolvem o problema aplicando o modelo de design apropriado ao caso.

Dificilmente um software perfeito livre de defeitos é produzido por alguém. O que pode ser feito para minimizar os defeitos no fim do processo e em sua manutenção, é ter conhecimento dos motivos que levam um software a decompor. Um software suscetível à decomposição exala os seguintes odores:

Rigidez: Rigidez ocorre quando é necessário um grande esforço para alterá-lo, mesmo quando a alteração é pequena.

Fragilidade: Fragilidade é a tendência que o programa tem para dar problema em diversos pontos quando uma única alteração é feita.

Imobilidade: Imobilidade ocorre quando o software contém muitas partes que podem ser reaproveitadas em outros softwares, mas para reutilizar essas partes é necessário um grande esforço junto com um alto risco, pois não foram planejadas para ser reutilizadas. Logo, uma grande parte do sistema terá que ser levada junto sem necessidade nenhuma.

Viscosidade: Um projeto viscoso é aquele que quando uma alteração é necessária, devido ao seu design não planejado para trabalhar de forma componentizada, fica difícil fazer a alteração do modo correto, gerando desvios para que a alteração seja mais fácil e mais rápida de programar.

Complexidade desnecessária: Acontece quando o design do sistema é superdimensionado e implementado para prever situações que nem sequer foram especificadas. Isso torna o software complexo demais e difícil de entender.

Redundância desnecessária: Acontece muito quando o programador necessita do mesmo código utilizado em outra função para resolver um problema, porém com algumas alterações. Acontece que isso acaba gerando redundância de código tornando a manutenção difícil e trabalhosa já que o erro deve ser corrigido em todas as repetições de código.

Opacidade: Opacidade é a falta de clareza na hora de escrever o código fonte. Muitas vezes o código é legível apenas para quem o programou. Isso pode ser evitado utilizando padrões na hora de escrever o código e fazendo com que outras pessoas revisem o código.

Todos esses odores são pontos extremamente importantes que devem ser considerados ao desenvolver um software. Em ambientes não ágeis, o design de um sistema degrada pois os requisitos mudam de uma forma que o design inicial não previa causando a mudança do design original, tornando-o desorganizado e ineficaz.

Um time ágil investe um pouco a mais no design inicial para tornar o sistema resistente a mudanças evitando que ele apodreça.
Nos próximos posts vou falar sobre os princípios de design ágil que podem ser utilizados para evitar o apodrecimento do software. Esses princípios são: Divisão de responsabilidades, Aberto / Fechado, Substituição de Liskov, Inversão de dependências e Segregação de interfaces.

Regras gerais de nomeação de identificadores

Segue algumas convenções gerais de como escolher a palavra que será o nome de um identificador.

 - O nome escolhido deve estar sempre ligado ao propósito do identificador. Deve ficar claro o que o identificador faz ou representa logo na primeira leitura do seu nome.
 
 - Esqueça as abreviações e dê preferência para a clareza do nome. Abreviações não irão tornar seu código mais rápido. Ex. NomeDoFuncionário é mais fácil de entender que nmFunc.
 
 - Não utilize hífens ou outros separadores de palavras nos nomes. Utilize PascalCase ou camelCase dependendo da situação.
 
 - Esqueça a Notação Húngara.
 
 - Tente evitar nomes que conflitem com palavras chave ou reservadas de outras linguagens de programação. Isso ajudará a evitar muitas dores de cabeça case seja necessário migrar o código para outras linguagens.
 
 - Se for utilizar uma sigla, não invente, utilize apenas se a mesma for largamente conhecida. Ex. Html, Xml.

 - Dê preferência para a utilização de nomes genéricos da CLR ao invés de nomes específicos da linguagem. Ex. Uma método que converte algo para boleano deve se chamar ConvertToBoolean e não ConvertToBool, pois Boolean é o nome base na CLR que é representado por bool em C#.

 - Use um nome comum como value ou item quando um identificador é algo que não possui um propósito tão específico. Ex. void Gravar(double value) ; void Gravar(int value) ; void Gravar(short value) ; 
 
 - Quando for criar uma nova versão de uma API, crie um nome similar ao original. No novo nome, opte por utilizar sufixos ao invés de prefixos, assim ficará mais fácil de achar a nova função através do Intellisense. Se o nome antigo é o único que faz sentido para aquele propósito, utilize um sufixo numérico. Ex. public class Carro {...}  ->  public class Carro2 ou public class Automóvel.

 - Nos casos dos métodos obsoletos, utilize o atributo [Obsolete(...)].


Referencias:

PascalCasing e camelCasing

Para a nomeação de identificadores, existem duas formas mais apropriadas: PascalCasing camelCasing


- PascalCasing: É utilizado para em Namespaces, tipos, e variáveis que possuem mais de uma palavra. Consiste em capitalizar a primeira letra de cada palavra. 

Ex: NomeDoFuncionário

No caso de siglas composta por 2 letras, ambas as letras devem ser capitalizadas. 

Ex: IOStream onde IO é uma sigla para input/output.

- camelCasing: É utilizado apenas para parâmetros. Consiste em capitalizar a primeira letra de cada palavra com exceção da primeira palavra. 

Ex: nomeDoFuncionário 

No caso de siglas composta por 2 letras, ambas as letras devem ser minúsculas. 

Ex: ioStream onde IO é uma sigla para input/output. 


Maiores informações em:.NET Framework Developer's Guide - Capitalization Conventions

Naming Guidelines

O que são "Naming Guidelines"

Bom, são diretrizes para nomear as coisas... hum, essa não foi muito boa. Vamos mergulhar mais fundo então. 

Quando codificamos, nos deparamos com um universo de coisas que precisam de um nome. Estas 'coisas' são chamadas de Identificadores. Um identificador é tudo que não se enquadra como palavra reservada, símbolos ou números. Por exemplo, nomes de Assemblies, DLLs, Namespaces, Clases, Estruturas, Interfaces, Variáveis, etc.

Um ex-professor sempre me dizia: "O que custa reservar um tempo para inventar um bom nome? Lembre-se que sua mãe demorou 9 meses para te dar um!" 

Para entender a importância dos nomes, imagine um sistema com 26 variáveis cujo padrão de nomenclatura utilizado foi o de "A" a "Z", ou seja, a primeira variável tem o nome de "A" e a última de "Z" 

Agora imagine se der algum problema na variável "K", quanto tempo demoraria para lembrar que a variável "K" armazena o nome do funcionário? 

Agora um pesadelo, imagine que o sistema foi feito por um colega de trabalho que já saiu da empresa e que o escolhido para dar manutenção neste sistema foi você! 

Acho que já deu pra ter uma idéia da importância de se adotar um conjunto consistente de diretrizes de nomenclatura no desenvolvimento de um software. Essas diretrizes possuem um propósito muito importante quando falamos de usabilidade e manutenibilidade do software. 

Tentarei passar nos próximos posts algumas técnicas de nomenclatura de identificadores e como utilizá-las. 

Abraços,

Livro: Agile Principles, Patterns, and Practices in C# (Robert C. Martin Series)



Agile Principles, Patterns, and Practices in C# (Robert C. Martin Series)

Este é um ótimo livro pra que quer aprender mais a respeito de como desenvolver uma boa arquitetura utilizando metodologia ágil. 
Creio que irei postar aqui muitas coisas interessantes que estão neste livro, principalmente na parte de arquitetura.

Hello World!

Olá a todos!

Seja bem vindo ao meu novo blog!

Decidi criar este blog para compartilhar os meus conhecimentos com todos, já que grande parte do que conheço aprendi em blogs como este. 

Meu objetivo aqui neste blog é expor informações sobre Arquitetura de Software, Melhores Práticas, testes, desenvolvimento entre outras informações que utilizam a tecnologia Microsoft.Net.

Espero que gostem!

Denis Yomura