8 Princípios de Design para SOA

Desenvolvimento de software, independente do paradigma, seja ele estrutural, orientado a objeto, componentizado, web, client-server e etc, nunca foi um trabalho trivial e com uma espécie de receita de bolo para o sucesso. Costumo dizer que a arte de construir sistemas duráveis e com retorno de investimento é muito árduo e as chances são mais favoráveis ao insucesso do que ao sucesso. Construção de software voltado ao paradigma de orientação a serviço traz riscos ainda maiores.

Isso ocorre porque uma arquitetura SOA geralmente se estende por várias áreas de negócio e requer uma análise inicial bem considerável. Como arquitetura SOA faz parte da evolução dos modelos arquiteturais, digo isso porque para existir SOA precisava primeiramente existir o conceito de componentes distribuídos, arquitetura modular, conceito de reutilização fortemente apoiada na programação orientada a objetos, protocolos interoperáveis favorecidos pela web e por aí vai. Portanto, toda as boas práticas de engenharia para construção de software continua existindo e sendo ainda mais cobrado e exigido para se obter uma arquitetura SOA funcional e de sucesso. Thomas Erl em seu livro SOA Principles of Service Design categorizou 8 princípios fundamentais para guiar a construção de serviços, no qual alguns deles já faziam parte da velha academia, mas que consolidado com outras regras se tornam um conjunto de regras básicas para SOA (guideline).

1. Standardized service contract

Contrato padronizado é um princípio aplicado dentro do paradigma de design de serviços, que fornece por meio de um padrão, que os serviços sejam registrados em inventários. Um contrato de serviço representa um artefato fundamental, no qual através dele serviços interagem uns com os outros e com potenciais consumidores. Portanto, existe uma forte necessidade de padronizar os contratos de serviços, a fim de tornar os serviços reutilizáveis, recompostos e interoperáveis. Exemplo de padrão é o XML Schema WSDL, contrato de serviço WS* que contém a descrição do serviços com documento(s) de política, para que se possa ser compreendido em tempo de design fornecendo as 03 áreas de um contrato, veja abaixo:

Padronização de expressão funcional Operações de serviço devem ser definidos com nomenclatura padronizadas, extendendo-se para os nomes de entrada, saída das mensagens e seus tipos correspondentes. Isso ajuda na interpretação do contrato, que por sua vez aumenta a reutilização e interoperabilidade de serviços evitando duplicidade por falta de entendimento.
Padronização do modelo de dado Como reutilização é um dos pilares, provavelmente utilizaremos uma estrutura de dados que atende um requisito de negócio em lugares diferentes, exemplo: pedido de venda. Para que essa entidade não tenha estruturas diferentes em contextos diferentes, precisamos que o mesmo tenha uma estrutura padrão que represente o requisito funcional e seja centralizado para reutilização corporativa e elimine qualquer sobrecarga de transformação, facilitado a interoperabilidade e compartilhamento, bem como o uso de schemas.
Padronização de política Políticas representam os termos de uso de um serviço. Para que um serviço seja reutilizável, seus requisitos de comportamento precisam ser exposto de forma consistente com uso de políticas padronizadas, baseadas nos padrões da indústria. Este tipo de padronização promove maior separação das políticas de contratos de serviços em documentos de políticas.

2. Service loose coupling

Dentro do paradigma de design, baixo acoplamento é um princípio de projeto que é aplicado aos serviços para garantir que o contrato de serviço não esteja fortemente ligado aos consumidores, nem com a lógica de serviço subjacente e a sua implementação. Isso resulta em contratos de serviços que possam ser evoluídos livremente, sem afetar os consumidores de serviços ou a sua implementação.

Contract-to-logic Quando o contrato é gerado com base em lógica existente, às vezes até por meio de ferramentas automatizadas. Isso inibe a evolução do contrato de serviço, pois o contrato não está concebido de forma independente de acordo com as normas de concepção e é ditada pela lógica subjacente.
Contract-to-implementation Quando os contratos são projetados de forma que eles se baseiam nos detalhes de implementação subjacentes, por exemplo, uso de modelos de dados utilizados no banco de dados subjacente. Desta forma, uma alteração na implementação subjacente exige uma alteração correspondente no contrato de serviço. Uma forma de evitar esse acoplamento é o uso de uma service façade.
Contract-to-technology Contrato que expõe elementos de tecnologia utilizada ​​pelo serviço. Exemplo: contrato baseado em WCF ou EJB. Dessa forma os consumidores estão acoplados a uma tecnologia particular, dificultando a capacidade do serviço em ser interoperável.
Contract-to-functional Quando o contrato do serviço é desenvolvido para um determinado consumidor em mente. Exemplo: serviços construídos para permitir a comunicação com um parceiro, serviço que executam uma parte da lógica do processo ou é um serviço controlador de uma composição de serviços que executa a lógica do processo de negócios.
Consumer-to-implementation Quando os consumidores acessam o serviço diretamente através da lógica ou implementação. Isso pode acontecer quando os consumidores acessam o serviço através de interfaces de implemetação antes que o contrato se torna-se uma serviço propriamente. Contract-Centralization soluciona esse problema.
Consumer-to-contract Este é um tipo de acoplamento que ajuda a evoluir o serviço sem impactar os seus consumidores. Mas é importante que este acoplamento seja restrito ao contrato de serviço e não vaze para a arquitetura, caso contrário o consumidor do serviço pode facilmente tornar-se acoplado à implementação do serviço, a lógica ou tecnologia.

3. Service abstraction

Um contrato de serviço que contém detalhes sobre o que é, pode acabar sendo utilizado de uma maneira particular devido o alto conhecimento sobre o funcionamento do serviço por parte do consumidor. Isso pode afetar a evolução do contrato de serviço, pois o consumidor está indiretamente acoplado à implementação do serviço, que pode precisar ser substituído no futuro. De certa forma, isso aumenta o acoplamento do tipo consumidor-contrato, que, embora seja um tipo positivo de acoplamento, demasiadamente impacta de forma negativa na evolução, tanto do prestador de serviços como o consumidor.

Functional abstraction Esse princípio visa um contrato otimizado que maximiza o potencial de reutilização do serviço. Na orientação a objetos, uma classe só iria expor métodos público que considere ser importante para seus objetos, os métodos auxiliares que não são relevantes para os objetos são mantidos em oculto. Um contrato de serviço que não tenha sido submetido a este princípio poderia ser denominado como um “contrato detalhado”, que revela muito de regras de negócios e da lógica de validação.
Technology information abstraction Qualquer informação sobre a tecnologia interna utilizada deve ser abstraída. Esta informação adicional pode resultar em consumidores que visam uma implementação particular, desenvolvendo assim acoplamento consumidor-implementação.
Logic abstraction Os detalhes sobre a programação da lógica precisa ser abstraído, como o conhecimento sobre como o serviço realmente executa sua funcionalidade pode resultar em consumidores que são projetados sob essas premissas. Isso pode dificultar seriamente os esforços refatoração e pode ser considerado como um anti-pattern.
Quality abstraction Abstração de qualidade relaciona-se com os detalhes fornecidos no prazo de SLA que acompanha o serviço. É importante concentrar-se apenas no tipo de informação que seria realmente útil em determinar a confiabilidade e a disponibilidade do serviço. Nenhuma outra informação deve ser incluída, expondo detalhes desnecessários. Por exemplo, detalhes de como funciona um serviço permanece dentro do processo global de negócios, bem como outros serviços que são utilizados para cumprir a sua funcionalidade. Apenas pessoas autorizadas devem ter acesso e uma política de QoS e detalhes do ambiente/tecnologia ou maiores detalhes do projeto.

4. Service reusability

Serviços reutilizáveis ​​são projetados para que sua lógica seja independente de qualquer processo de negócio específico ou tecnologia. Reutilização relaciona-se quando o serviço é utilizado para automatizar vários processos de negócio. Essa reutilização elimina a necessidade de criar um novo serviço completo e se torna uma parte de vários processos de negócios, sem fazer parte de qualquer processo de negócio particular. O princípio de reutilização de serviços aborda esses equívocos, fornecendo um conjunto de diretrizes que ajudam a projetar serviços que contêm lógica que não está ligada a qualquer processo de negócio em particular e, portanto, poderiam ser reutilizados em toda a empresa para a automatização de vários processos de negócio. Aplicação com composição de serviços, captação de serviços e serviços levemente acoplados ajudam a desenvolver serviços combináveis.

5. Service autonomy

Autonomia de serviços é um princípio que visa fornecer serviços com independência de seu ambiente de execução. Isso resulta em maior confiabilidade, já que os serviços podem operar com menos dependência de recursos sobre os quais há pouco ou nenhum controle. Confiabilidade é crítico para garantir a longevidade do serviço.

Design-time autonomy Autonomia em tempo de design se refere à independência que os serviços tem de modo que, possam ser evoluídos sem impactar nos seus consumidores. Esse tipo de autonomia é importante para manter baixo acomplamento, assim refatorações ou mudanças internas não impliquem em modificações no contrato do serviço.
Run-time autonomy Autonomia em tempo de execução refere-se ao controle que um serviço tem sobre a forma como a lógica da solução é processado pelo ambiente de tempo de execução. Quanto mais controle um serviço tiver sobre seu ambiente de tempo de execução, mais previsível é o seu comportamento. Autonomia de tempo de execução é obtido por meio de recursos de processamento dedicados. Por exemplo, se a lógica do serviço tem um uso intenso de memória, o serviço poderia ser usufruir de um servidor com recursos adicionais.
Service types Serviços precisam ser priorizados para que a sua autonomia possa ser abordada de acordo com o seu valor para o negócio. Isso pode ser feito analisando o contexto funcional do serviço. Serviços cujos contextos funcionais são independentes de qualquer processo de negócio em particular, por exemplo, serviços que oferecem funcionalidades que é de interesse para os diferentes tipos de consumidores. Por outro lado, os serviços específicos de processos de negócios, por exemplo, tarefas e tarefa orquestradas, são menos reutilizáveis e dependem da autonomia individual dos seus serviços compostos.

6. Service statelessness

Interação entre softwares ou requisitos de negócio muitas vezes exigem o esforço de manter e gerenciar o controle do estado das informações entre as operações. Isso se torna mais importante em arquiteturas distribuídas onde o cliente e o servidor não estão fisicamente na mesma máquina. Numa composição de serviço, um serviço pode armazenar dados específicos da atividade em memória enquanto ele espera um outro serviço completar seu processamento. Gestão eficiente da atividade de serviço relacionado aos dados se torna mais importante como um serviço que visa a reutilização do mesmo. As diretrizes de arquitetura SOA é construir serviços stateless, deslocando a sobrecarga de gerenciamento de estado dos serviços para um outro componente/middleware externo. Isso ajuda ainda mais na escalabilidade global da solução.

7. Service discoverability

O potencial de reutilização de um software não pode ser conseguido se não se sabe ainda se existe ou não, por isso ser detectável é algo muito importante. Não somente detectável, mas que também seja corretamente compreendido, ou seja, depende da qualidade da meta-informação. No caso de uma solução orientada a serviços, por causa da ênfase dada à reutilização de serviços, é muito claro que as oportunidades devem existir para a sua reutilização, o que só é possível se forem descobertos. Para tornar os serviços detectáveis, precisamos algumas atividades:

– Documentar os o serviço de forma consistente.
– Armazenar a informação documentada em um repositório pesquisável (UDDI).
– Habilitar a procurar da informação documentada de uma maneira eficiente.

Além de aumentar o potencial de reutilização dos serviços, o mecanismo de descoberta também é necessário para evitar o desenvolvimento de uma solução que já está contida em um serviço existente. Para projetar os serviços que não são apenas detectáveis, mas também fornecer informações interpretáveis ​​sobre suas capacidades, o princípio de descoberta do serviço fornece diretrizes que poderiam ser aplicados durante a fase de análise do processo de prestação de serviços.

8. Service composability

Desenvolvimento de software a partir de componentes independentementes incentivam o conceito de composição. Este é o um conceito implícito na orientação a objetos, onde o produto final é composto de vários objetos interligados que têm a capacidade de se tornar parte de múltiplas soluções. O mesmo conceito de composição é utilizado em SOA por meio de um processo de negócio automatizado, através da combinação de vários serviços, Exemplo: BPEL. O princípio de modularidade de serviço fornece considerações de design que ajudam na concepção de serviços combináveis ​​com o objetivo de incentivar a reutilização o máximo possível. A diretriz é que um serviço esteja sempre pronto para participar de composições de serviços sem a necessidade de quaisquer alterações no design.

O que é arquiteto de software?

Arquitetura, sem dúvida um assunto pelo qual e apaixonei há aproximadamente 10 anos atrás. Mas antes de falar de software, queria refletir sobre, o que é arquitetura? A palavra “arquitetura” significa muitas coisas diferentes para muitas pessoas, com muitas definições diferentes. Algumas delas são: O sistema como um todo, ferramentas e métodos, caminho dos requisitos para que o produto final, princípios orientadores, liderança técnica, relação entre os elementos que compõem o produto, consciência das limitações e restrições, alicerce, visão abstrata, decomposição do problema em elementos menores, esqueleto do produto entre outros. Não é à toa que é difícil encontrar uma única definição. Existem dois temas comuns; arquitetura como um substantivo e arquitetura como um verbo, ambos são independentemente aplicáveis quando falamos de construção de um edifício ou um software.

WhatsArchitecture

Como substantivo, a arquitetura pode ser resumida como sendo a estrutura e a decomposição de um produto em um conjunto de componentes e interações. Como verbo, arquitetura (ou seja, o processo, arquitetar) é o que você precisa para construir, criando uma visão para construí-la e tomar decisões de design. Fundamentalmente, se refere a comunicação entre as pessoas envolvidas na construção do produto, compreendendo a visão e contribuindo de uma forma positiva para o sucesso. Simplificando, o processo de arquitetura é sobre a introdução de liderança técnica. Há muitos tipos diferentes de arquitetura na indústria de TI, os mais populares são:

Infrastructure Security Technical Solution
Network Data Hardware Enterprise
Application System Integration IT
Software Information Process Business

Alguns dos tipos de arquitetura mencionadas são claros, porém algumas dependem de outro tipo para a sua definição. Por exemplo, o que significa arquitetura da solução? Para algumas empresas “arquiteto de soluções” é simplesmente um sinônimo de “arquiteto de software”, enquanto outros têm um papel específico que concentram-se na concepção de uma “solução” para um problema global, parando antes que o nível de detalhes da implementação são discutidos. Da mesma forma, arquitetura técnica é vago, podendo ser referido a hardware, software ou uma combinação dos dois.

O que todos esses termos têm em comum, então? A princípio todos estes tipos de arquitetura têm estrutura e visão em comum. Peguemos arquitetura de infraestrutura como um exemplo, imagine que você precisa criar uma rede entre dois escritórios em diferentes extremos do país. Uma opção é encontrar o maior rolo de cabo de rede que você pode e começar a ligar um escritório ao outro em uma linha reta. Supondo que você tenha cabo suficiente, isso poderia funcionar, mas na realidade, há um certo número de limitações ambientais e não funcionais. Características que você precisa considerar a fim de realmente oferecer algo que satisfaça o objetivo original. Este é o lugar onde o processo de arquitetura atua, para uma visão para alcançar o objetivo é importante.

Um único e longo pedaço de cabo é uma abordagem, mas não é muito bom por causa de restrições do mundo real. Por esta razão, as redes são tipicamente muito mais complexas e exigem um conjunto de componentes que colaborarm em conjunto para satisfazer a meta. Do ponto de vista de infraestrutura, então podemos falar sobre estrutura em termos de componentes comuns que você esperaria ver dentro deste domínio, as coisas como roteadores, firewalls, proxies, switches e etc. Independentemente se você está construindo um sistema de software, uma rede ou um banco de dados, uma solução bem sucedida requer que você entenda o problema e crie uma visão que possa ser comunicada a todos os envolvidos com a construção do produto final. Arquitetura, independentemente do domínio, atua diretamente na estrutura e visão.

Arquitetura de Software

À primeira vista, “arquitetura sofware” parece uma coisa fácil de definir. É sobre a arquitetura de um pedaço de software, certo? Bem… na verdade é mais do que apenas software. Ao contrário de aplicação e arquitetura do sistema, que são relativamente bem compreendido, o termo “arquitetura de software” tem muitos significados diferentes para muitas pessoas diferentes. Ao invés de ficar amarrado nas complexidades e nuances das muitas definições de arquitetura de software, eu gosto de manter a definição mais simples possível. Para mim, arquitetura de software é simplesmente a combinação de arquitetura de aplicação e arquitetura de sistema.

ArchitectureSoftware

Em outras palavras, é tudo e qualquer coisa relacionada com os elementos significativos de um sistema de software, a partir do design e fundamentos de código até a implantação bem-sucedida do mesmo em um ambiente ao real. Por esta razão, a arquitetura de software é, ironicamente, sobre o software e hardware. Assim, dizemos que as decisões relevantes são “arquitetura” e que tudo o resto é “design”.

Grady Booch definiu a diferença de arquitetura e design de uma forma que realmente nos ajuda a responder a esta pergunta. “Como substantivo, design é o nome da estrutura ou o comportamento de um sistema cuja presença resolve ou contribui para a resolução de uma força ou forças no sistema. Um projeto representa, portanto, um ponto em um espaço de decisão em potencial”. Se pensarmos sobre qualquer problema que precisamos resolver, há provavelmente inumeras maneiras em que podemos resolvê-lo. Há provavelmente um número de diferentes tecnologias, plataformas de implantação e abordagens de design que também são opções viáveis para alcançar o mesmo objetivo. Para encerrar, quero destacar a definição de arquitetura por Martin Fowler, “As decisões de arquitetura são aquelas que você não pode inverter sem algum grau de esforço”. Ou seja, arquitetura de software é tudo aquilo que dói, é impossível ou inviável mudar mais tarde.

Websocket e uma nova web

Desde o boom da web e o nascimento das grandes empresas .com, os desenvolvedores têm enfrentado diversos desafios e adotado inúmeras práticas afim de contornar problemas das limitações do modelo original da arquitetura web. Por exemplo: Server Session, XmlHttpRequest-Ajax, Long Pooling, Frame Forever, Server Sent Event, Comet, CDNs, Bundles entre outros. Esse modelo arquitetural popularmente conhecido como Request-Response, originalmente stateless que utiliza protocolo http/https não foi projetado para cenários de aplicações real time.

O atual protocolo HTTP é lento para muitos cenários ou requisitos não-funcionais de muitos sistemas, porque necessita obrigatoriamente de uma solicitação ao servidor web e no aguardo de um documento por parte do servidor para que seja exibido em uma página web. Após 15 anos dessa consolidação da web como plataforma quase que padrão para os sistemas atuais, e o mundo vivenciando o fenômeno chamado Cloud Computing, surge então o WebSockets, propondo uma nova abordagem sobre o modelo arquitetural padrão de sistemas web.

WebSocket é um protocolo de comunicação parte da iniciativa do HTML 5, cujo o objetivo é permitir que aplicativos na web possam entregar notificações em tempo real, aproximando-se do modelo arquitetural Client-Server, popular nos anos 80 com aplicações Thin Clients e Desktop Applications. WebSockets permite que navegadores possam abrir um canal de comunicação full-duplex bidirecional com os serviços, onde ambas as extremidades usufruem deste canal de comunicação para enviar imediatamente uma notificação de um endpoint para o outro endpoint. O mesmo é suportado em vários navegadores, incluindo o Google Chrome, Internet Explorer, Firefox, Safari e Opera.

Websocket

O protocolo websocket define duas novas URI schemes que são similares ao HTTP schemes. “ws://” host [ “:” port ] path [ “?” query ] é baseado no “http:” scheme padrão, utilizando a porta 80 sem segurança no nível do transporte. “wss://” host [ “:” port ] path [ “?” query ] é baseado no protocolo “https:” scheme, que utiliza a porta 443, com conexões seguras com túnel criptografado SSL. Assim, quando proxies ou intermediários de rede estiverem presentes na infraestrutura, não haverá problemas de compatibilidade e permitirá que conexões seguras seja bem sucedida e requisitos de segurança sejam atendidos. O seguinte trecho de código seguro estabelece uma conexão WebSocket:

var host = "ws://example.baccaro.com.br";
var socket = new WebSocket(host); 

Exemplo da API Websocket

 var host = "ws://qualquercoisa/echo";
 var socket = new WebSocket(host);
 socket.onopen = function (openEvent) {
    $("#serverStatus").text('Socket Open');
 };
 
 socket.onmessage = function (messageEvent) {
 
    if (messageEvent.data instanceof Blob) {
      
      var destinationCanvas = $("#destination");
      var destinationContext = destinationCanvas.getContext("2d");
      var image = new Image();
    
      image.onload = function () {
         destinationContext.clearRect(0, 0, 
         destinationCanvas.width, destinationCanvas.height);
         destinationContext.drawImage(image, 0, 0);
      }

      image.src = URL.createObjectURL(messageEvent.data);

   } else {
      $("#serverResponse").text(messageEvent.data);
   };
 
 socket.onerror = function (errorEvent) {
    $("#serverStatus").text("Error was reported");
 };
 
 socket.onclose = function (closeEvent) {
    $("#serverStatus").text("Socket Closed");
 };

 function sendTextMessage() {
    if (socket.readyState != WebSocket.OPEN) return;
    socket.send($("#textmessage").val());
 }
 
 function sendBinaryMessage() {
    if (socket.readyState != WebSocket.OPEN) return;
    socket.send($("source").msToBlob());
 }

socket.send($("#msgText").val());
WebSocket Objeto Provides bidirectional channel to remote host.
close Método Closes a websocket.
send Método Sends data to a server using a websocket.
binaryType Atributo Binary data format received by onmessage.
bufferedAmount Atributo Number of data bytes queued using send
onclose Atributo Event handler called when the socket is closed.
onerror Atributo Event handler called when there is an error.
onmessage Atributo Event handler to notify that a message is received.
onopen Atributo Event handler called if the websocket connected.
protocol Atributo Reports the protocol the server selected.
readyState Atributo Reports the state of the websocket connection.
url Atributo Reports the current URL of the socket.

Agora podemos fazer muitas das coisas que gostamos, tais como: implementação inteligente de publisher-subscriber, observer, programação por evento, uso do conceito “Não nos chame, deixe que nós chamamos você!”, dentre outros padrões e conceitos, mas de um modo mais eficiente e sem workarounds. Para uso de Websocket na plataforma Microsoft ASP.NET, temos a biblioteca chamada SignalR, que é uma API de alto nível muito simples de implementar e fazer consumo de chamadas RPC do servidor para o cliente, bem como recursos adicionais de gerenciamento de conexão e notificação para conexão individual ou para todos simultâneamente. Como nem todos os browsers ou servidores web suportam Websocket, o SignalR faz essa verificação e implementa a técnica apropriada para que notificação server to client acontece, fazendo por baixo dos panos hora implementação de long polling, hora frame forever, hora Websocket se ambas as pontas suportam. Pretendo falar de SignalR com maiores detalhes em um próximo post, fui!

Null Object Pattern

Quando desenvolvemos classes que têm dependências, em alguns contextos a dependência não é necessária. Uma forma de lidar com esta situação é a utilização de uma referência nula (null), em vez de um objeto real. Infelizmente, o uso nulo tende a complicar o código. Onde quer que você precisa usar um membro do objeto, você deve primeiro verificar se ele é nulo, caso contrário, ao invocar um método ou acessar uma propriedade de uma referência nula ocorrerá uma exceção.

O padrão Null Object proporciona uma solução para este problema. Em vez de passar referências nulas e aplicar a validação de objeto null antes de qualquer operação, você pode criar uma classe específica que representa uma dependência não-funcional. Esta classe implementa uma interface esperada ou herda de uma classe abstrata, mas não inclui nenhuma funcionalidade. Seus métodos e propriedades não executam nenhuma ação e seu possível retorno contém valores fixos pré-estabelecidos. Isso permite que qualquer objeto dependente utilize as dependências sem a realização de qualquer pré-verificações, simplificando o código.

Null Object é geralmente usado com outros padrões de projeto. A própria classe Null Object é muitas vezes criada como um Singleton. Isso limita o número de instâncias da mesma, sendo ideal por geralmente não ter nenhum estado e nenhuma funcionalidade, evitando assim a sobrecarga desnecessária para o software. Outro padrão de design que é freqüentemente encontrado com o padrão de Null Object é o Strategy. Quando uma das estratégias não requer funcionalidade, um Null Object pode ser usado. Um terceiro padrão associado comum é o pattern Factory Method, onde a factory pode retornar uma instância de Null Object.

Um exemplo da simples da utilização desse padrão seria um subsistema utilitário para gravação de log ou trace de uma aplicação. Utilizando o pattern strategy poderíamos fornecer dependências para as várias opções possíveis para o utilitário de log. Por exemplo: gravação de log em App Console, em banco de dados, envio para outro aplicativo via socket, gravação direta em arquivo de sistemas, envio por e-mail e etc. Se em algumas situações você não precisa informar desativar o sistema de log, uma das opções do strategy seria um Null Object. Veja o exemplo abaixo:

NullObjectPattenr

Estrutura:

Client Esta classe tem uma dependência que pode ou não ser necessária. Quando for necessária nenhuma funcionalidade da dependência, ela irá executar os métodos de um objeto nulo.
Dependency Base Essa classe abstrata é a classe base para as várias dependências disponíveis que o cliente pode usar. Quando a classe base não fornece funcionalidade compartilhada, opte por uma Interface.
Dependency Esta classe é uma dependência funcional que pode ser utilizada pelo cliente.
Null Object Esta é a classe representante de uma referência nula, que pode ser utilizado como uma dependência pelo cliente. Ele não contém nenhuma funcionalidade, mas implementa todos os membros definidos pela classe abstrata DependencyBase.

Resumo
Tendo em conta que uma referência de objeto pode ser nula, e que o resultado de uma verificação isnull é não fazer nada ou usar algum valor padrão, Na ausência de um objeto, cuja a referência nula, como podemos tratar de forma transparente? A intenção de um Null Object é encapsular a ausência de um objeto, fornecendo uma alternativa substituível que oferece um padrão adequado sem alterar o comportamento. That’s All Folks.

Separation of Concerns

O princípio da separação de interesses propõe que os elementos de um sistema devem ter exclusividade e singularidade de propósitos. Ou seja, nenhum elemento deve compartilhar as responsabilidades de outro. A separação de interesses é obtido através de limites bem estabelecidos. A fronteira de separação se baseia em delimitações físicas ou lógicas, que determinam o conjunto das responsabilidades. Alguns exemplos de limites incluem o uso de métodos, objetos, componentes e serviços que definem o comportamento core de uma aplicação; projetos, soluções e camadas de organização de processamento.

O processo de realização de separação de conceitos envolve a divisão de um conjunto de funções, cujo o objetivo não é reduzir um sistema em partes indivisíveis, mas sim a organização do sistema em elementos não repetitivos e de responsabilidades coesas. Separação de interesses tem sua essência na ordem, cujo o objetivo geral é o estabelecimento de um sistema bem organizado, onde cada parte cumpre um papel significativo e intuitivo, maximizando sua capacidade de se adaptar às mudanças. Aplicabilidade desse princípio pode resultar em alguns benefícios como:

  1. A ausência de duplicação e singularidade do efeito dos componentes individuais tornam o sistema global mais fácil de manter.
  2. O sistema como um todo, torna-se mais estável, como um subproduto do aumento da capacidade de manutenção.
  3. As estratégias necessárias para garantir que cada componente só se preocupa com um único conjunto de responsabilidades coesas muitas vezes resultam em pontos de extensibilidade naturais.
  4. A dissociação que resulta da necessidade de componentes para se concentrar em um único propósito leva a componentes que são mais facilmente reutilizados em outros sistemas ou contextos diferentes dentro do mesmo sistema.
  5. O aumento da manutenção e extensão pode ter um grande impacto sobre a taxa do sistema de comercialização e aprovação.

O princípio da separação também é benéfico quando aplicada dentro das empresas, garantindo que os grupos e sub-organizações são atribuídos a um conjunto único de responsabilidades de coesão. Isso falicita os objetivos gerais de negócios, minimizando coordenação entre as equipes e maximizando o potencial de cada equipe ao se concentrar em sua responsabilidade coletiva, centro de competência e melhorando a resolução de problemas em grandes sistemas. Quando as responsabilidades estão devidamente delineadas, a identificação do problema se torna mais fácil e a resolução se torna mais rápida.

Cada uma destas áreas, por sua vez contribui para o processo de controle de qualidade melhorada. Se as organizações de pessoas ou sistemas de software estão em vista, aplicando o princípio da separação de interesses pode ajudar na gestão da complexidade, eliminando duplicações desnecessárias e alocação de responsabilidade própria. Nas próximas seções, várias técnicas serão discutidas para conseguir a separação de interesses dentro de design do aplicativo.

Separação de conceitos de modo vertical
Refere-se ao processo de dividir uma aplicação módulos que se relacionam dentro de um aplicativo. Separação vertical divide os recursos de um aplicativo de forma holística, associando qualquer interface do usuário, processamento de negócio e qualquer coisa que corresponde á acesso de recursos dentro de um único limite. O exemplo abaixo ilustra a separação de módulos:

SeparateConcerns03

Separando as características de uma aplicação em módulos esclarece o responsável e dependências de cada característica que pode ajudar no teste e manutenção em geral. Os limites podem ser definidos de forma lógica para ajudar na organização, ou fisicamente para permitir o desenvolvimento e manutenção independente (gosto muito dessa opção).

– Limites lógicos: implica a existência de modularidade, que pode ser útil para melhorar a capacidade de manutenção de uma aplicação.

– Limites físicos: geralmente usado no contexto de desenvolvimento de add-ins ou aplicações compostas, e pode permitir que recursos sejam geridos por equipes de desenvolvimento diferentes. Aplicações que suportam add-in módulos muitas vezes empregam técnicas como a auto-discovery, ou inicialização de módulos baseados em configuração externa.

Separação de conceitos de modo horizontal
Refere-se ao processo de dividir uma aplicação em camadas lógicas funcionais que exercem o mesmo papel dentro do aplicativo. Uma divisão comum nas aplicações é a separação dos processos em camadas de apresentação, negócios e acesso a dados e recursos. Estas categorias englobam os principais tipos de preocupações para a maioria das necessidades de aplicação, e representam uma organização que minimiza o nível de dependência dentro de um aplicativo. Veja os exemplos abaixo:

SeparateConcerns02

SeparateConcerns01

Separação de conceitos por aspecto
Conhecida como programação orientada a aspectos AOP, refere-se ao processo de segregação transversal de um aplicativo a partir de suas preocupações centrais. Preocupações transversais são preocupações que são intercaladas entre vários limites dentro de um aplicativo. Log é um exemplo de uma atividade realizada através de muitos componentes do sistema. A diferença de AOP com as demais técnicas de separação é por se basear em um pré-processamento, tempo de compilação ou fusão dos conceitos transversais em runtime. O processo de fusão fusão dos conceitos cross-cutting em background é conhecido como “weaving”. Utilizando-se de várias estratégias, conceitos transversais podem ser separados e acoplados de volta na aplicação com coesão em tempo de runtime.

SeparateConcernsAop

Direção da dependência
Uma característica de uma boa separação de conceitos é a definição da direção da dependência. A direção da dependência estabelece os papéis do consumidor e o da dependência, de modo que o papel da dependência tem maior potencial de reutilização. O conceito de direção da dependência é a relação comum entre um componente de negócios e um componente utilitário. Considere um sistema que fornece um processo de pesquisa de vendas, que exige que os dados pesquisados frequentemente sejam armazenados em cache para maior eficiência. Para facilitar a persistência das informações de venda, um componente de cache pode ser desenvolvido para separar as preocupações de cache do processo de pesquisa de vendas no sistema. Dado que a função de armazenamento em cache é um comportamento mais genérico do que um processo de consulta de vendas, o componente de armazenamento em cache tem o maior potencial de reutilização entre os dois. Portanto, a melhor direção a dependência, neste caso, seria a partir do componente de negócios para o componente utilitário.

A conlusão é que separar conceitos faz dos software muito mais flexível, mais coeso, melhor reutilizado, com melhor manutenabilidade, testabilidade e etc, etc e etc!

Common Closure Principle

Um grande projeto de desenvolvimento é subdividido em inúmeros pacotes inter-relacionados presentes no release. O trabalho de gerenciar, testar e liberar esses pacotes não é assim tão trivial. Quanto mais os pacotes sofrem alterações, maior será o esforço para o build, teste e deploy. Por isso, é muito importante minimizar o número de pacotes alterados em qualquer ciclo de libertação do produto.

Para conseguir isso é necessário o agrupar as classes de forma inteligente, considerando suas responsabilidades, propósitos e que as mudanças necessárias façam sentido para um conjunto. Um mudança qualquer precisa fazer sentido para o conjunto, evitando alterações em um pacote de forma desnecessária. Isso requer senso de organização, antecipando os tipos de mudanças que são prováveis em um contexto. Uncle Bob faz mensão desse conceito baseado no Single Responsability Principle, no qual um classe deve ter apenas um motivo para ser alterada, se mais de um tipo de mudança afeta uma classe, isso significa que ela ela pode ser decomposta e está possuindo responsabilidade adicional.

“As classes em um pacote deve ser fechadas em um conjunto contra o mesmo tipo de mudança. Uma mudança que afeta um pacote todas as classes desse pacote, e ponto final.”

Dessa forma, na organização lógica (namespaces, packages) do projeto em busca de reuso de componente e separação de conceitos, as classes que são fortemente acopladas devem estar dentro do mesmo pacote. Assim uma alteração nesse pacote afetará o conjunto de forma coerente. Alguns exemplos são: Classes responsáveis pela persistência que lida diretamente com o providers de banco de dados relacionais. Em qualquer alteração nesse contexto de persistência de dados não deveria impactar no restante d o sistema. Uma pacote com entidades de negócio não deveria ter referência a uma biblioteca ajax web. Um contexto de negócio referente a autenticação e autorização de acesso precisaria se manter isolada dos demais contextos de negócio. Uma refatoração de negócio ou de tecnológica no logon do sistema deveria impactar somente nesse pacote específico.

Organizando os componentes e pacotes com coerência, favorecerá na implementação de novas features sem impactar nas existentes, sem quebrar uma implementação. Isso está relacionado com o princípio Aberto-Fechado que falarei posteriormente.

Classes Autônomas

As interdependências dificultam o entendimento de modelos e design, empobrecem os testes e a manutenção dos sistemas. Cada associação é uma dependência, para entender uma classe é necessário entender tudo aquilo a que ela está ligada. E cada agregado de uma classe pode estar ligado a outro agregrado, tornando um número ainda maior de coisas (entidades ou objetos de valor), que também devem ser compreendidas.

Módulo e Agregados têm por objetivo limitar o emaranhado de inter-dependências. Quando um subdomínio altamente coeso é esculpido e transformado em um módulo, um conjunto de objetos é desacoplado do resto do sistema de forma a se ter um número finito de conceitos interrelacionados. Até mesmo um módulo já requer bastante raciocínio sem mesmo haver um compromisso de controlar as dependências existentes dentro dele. A importancia da modularização é algo que aprendi a valorizar nos meus anos no desenvolvimento de softwares, levando em consideração a complexidade de manter o domínio de partes do sistema, onde cada sistema tem sua complexidade de negócio específico. Até mesmo com a compreensão e entendimento do modelo e negócio por parte de novos colaboradores.

Mesmo dentro de um módulo, a dificuldade de interpretar um design aumenta vertiginosamente à medida que as dependências são adicionadas. Isso acarreta em uma sobrecarga mental, limitando a complexidade do design que um desenvolvedor pode controlar. Conceitos implícitos contribuem para essa sobrecarga muito mais que as referências explícitas. Modelos refinados são destilados até que cada ligação remanescente entre os conceitos represente algo fundamental para o significado desses conceitos. Em um subconjunto importante, resultando em uma classe que pode ser completamente entendida por si só, junto com alguns tipos primitivos e conceitos básicos de biblioteca. Isso é uma arte!

Nas plataformas de programação, algumas noções básicas são tão abrangentes que sempre estão em mente. Por exemplo, os tipos primitivos e algumas bibliotecas fornecem noções básicas tais como números inteiros, strings e coleções, o que não gera sobrecarga mental. Embora possamos ignorar dependências em valores primitivos, não podemos ignorar o que eles representam. Por exemplo, em uma classe NotaFiscal poderíamos encontrar tipos primitivos para representar Pis, Cofins, Inss e Ir para nota de serviços, bem como, Icms e Ipi para nota de produto. A criação de um objeto Impostos com esses atributos não aumentaria o número de conceitos envolvidos nem as dependências. Porém, tornaria aqueles atributos existentes mais explícitos e mais fáceis de entender.

Baixo acoplamento é fundamental para o design de objetos. Refatore em busca do refinamento sempre que possível, elimimando todos os outros conceitos do cenário. Depois disso, a classe estará completamente independente e poderá ser estudada e compreendida isoladamente. Classes expressivas e objetivas tornam melhor o entendimento de um módulo do sistema.

Recado final, tente fatorar as computações mais complicadas transformando-as em Classes Autônomas, talvez através de modelagem de objetos de valor mantidos pelas classes mais ligadas. Exclua conceitos desnecessários, muitas vezes técnicos, como parte de uma arquitetura que empobrece o modelo do domínio. Exemplo desses são atributos de mecanismos de ORM, design patterns, membros de classes genéricas de frameworks proprietários e etc. Utilize o conceito dividir para conquistar, separe os conceitos, faça de uma grande aplicação complexa em pequenos módulos leves, portáveis, escaláveis, mais independente possível. O resultado será uma fácil manutenabilidade, segurança do domínio e um ponto favorável para o mundo da computação distribuída e SOA!

Anti-Corruption Layer Pattern

Novos sistemas quase sempre precisam ser integrado com sistemas legados, que possuem seus próprios modelos. Camadas de tradução pode ser simples, até mesmo elegante, quando bem projetada como uma ponte limitada. Quando um novo sistema que está a ser construído precisa ter uma interface com um outro, a dificuldade de se relacionar os dois modelos podem eventualmente sobrecarregar a intenção da modelagem do novo sistema completamente, fazendo com que ele seja modificado para se assemelhar ao modelo do outro sistema numa forma ad hoc. Os modelos de sistemas legados são geralmente fracos, e até mesmo exceções pode não atender às necessidades do projeto atual. No entanto, pode haver um alto valor a essa integração e que resulta em um requisito absoluto.

A resposta não é evitar integrações necessárias com outros sistemas, até porque é uma valiosa forma de reutilização. Em um grande projeto, um subsistema muitas vezes precisam interagir com vários outros subsistemas. Quando os sistemas baseados em diferentes modelos são combinado, a necessidade para o novo sistema de se adaptar a semântica do sistema pode levar a uma corrupção do modelo do sistema novo. Mesmo quando o outro sistema é bem concebido, não deixa de ser um outro modelo, e que ofertará perigo.

Há muitos obstáculos em interface com um sistema externo. Por exemplo, a camada de infraestrutura deve fornecer os meios para comunicar com outro sistema que pode estar em uma diferente plataforma ou utilizar diferentes protocolos. Os tipos de dados de outro sistema deve ser traduzido para as de seu sistema. Precisamos fornecer uma tradução entre as partes que aderem a modelos diferentes, de modo que os modelos não sejam corrompidos com elementos dos modelos externos. Portanto, crie uma camada de isolamento para fornecer a solução um blindagem para não contaminar seu modelo de domínio. A camada conversa com outro sistema por meio de sua interface existente, exigindo pouca ou nenhuma modificação para o outro sistema. Internamente, a camada traduz de forma bi-lateral, se necessário entre os dois modelos.

A interface pública do Anti-Corruption Layer geralmente aparece como um conjunto de serviços, embora ocasionalmente, pode assumir a forma de uma entidade. Construindo uma nova camada responsável pela tradução entre a semântica dos dois sistemas nos dá uma oportunidade para o abstrair o comportamento de outro sistema e oferecer seus serviços e informações para o nosso sistema de forma consistente com nosso modelo.

Ilustração de uso e aplicabilidade:
AntiCorruption

Uma maneira de organizar o uma Anti-Corruption Layer é como uma combinação de FAÇADE com ADAPTER patterns, combinados a tradutores e mecanismos de comunicação e transporte de mensagens. A Façade é uma alternativa que simplifica o acesso a um subsistema, no qual é exposto suas funcionalidades e oculto suas implementações e domínio. Um adaptador é um wrapper que permite que um client utilize um protocolo diferente do que é compreendido pelo implementador. Quando um client envia uma mensagem para um Adapter, ele é converte em uma mensagem semanticamente equivalente e enviado para o aplicativo “adaptado”.

AnticorruptionLayer

Considerações:

1. Se você não tem acesso a outro subsistema, você precisará utilizar uma façade pattern para interligar os sistemas. No entanto, se a façade pode ser integrada diretamente com o outro subsistema, então uma boa opção é colocar o link de comunicação entre o adapter pattern e a façade, pois o protocolo da façade é presumivelmente mais simples do que ela abrange.

2. Se você tiver acesso a outro subsistema, você pode se iludir achando que poucas refatorações são suficientes. Procure escrever as interfaces de forma explícitas para a funcionalidade que você vai usar, começando com testes automatizados, se possível.

3. Quando os requisitos de integração são extensos, o custo da tradução se eleva. Pode ser necessário fazer estreitamento no modelo do sistema em projeto ao sistema externo, a fim de facilitar a tradução. Faça isso com muito cuidado, sem comprometer a integridade do modelo. Se esta abordagem for a solução mais natural, considere fazer o seu do seu subsistema um Conformist Pattern, eliminando a tradução.

4. Se o subsistema é simples ou tem uma interface pública clara e bem definida, você não precisará de uma façade .

5. A funcionalidade pode ser adicionado à Anti-Corruption Layer se é específico para a relação entre os dois subsistemas. Tracking de auditoria para uso do sistema externo ou tracking da lógica para depurar as chamadas para a outra interface são duas características úteis.

Uma Anti-Corruption Layer tem objetivo de vincular dois Bounded Contexts. Há ainda situações em que faz sentido conectar dois subsistemas dentro de uma mesma solução, evitando uma corrupção do modelo, caso sejam baseados em diferentes modelos.

SCA – Service Component Architecture

Service Component Architecture é um conjunto de especificações destinada para o desenvolvimento de aplicações baseadas em arquitetura orientada a serviços (SOA), que define como entidades computacionais interagem para executar o trabalho para o outro. Originalmente publicado em novembro de 2005, a SCA é baseado na noção de que todas as funções de uma empresa devem existir na forma de serviços que são combinados em compósitos para atender aos requisitos de negócios específicos. SCA engloba diversas tecnologias, linguagens de programação, frameworks e plataformas de componentes de serviços Web e para os métodos utilizados para conectá-los. SCA pode ser dividido em quatro partes principais ou modelos:

Assembly Model Specification Define como os componentes são combinados, ligados e empacotados como serviços independentes da linguagem de programação. Este modelo define como especificar a estrutura de um aplicativo composto. Ele define quais serviços são montados em um (serviço de aplicação orientada a negócio) e com quais componentes esses serviços são implementados. O modelo de desenvolvimento é definido com arquivos XML.
Component Implementation Specifications Define como os serviços são empacotados e acessados ​​para linguagens de programação específicas. Essas especificações definem como um componente é na verdade escrito em uma linguagem de programação específica ex: Java, BPEL, C#, C++ e etc.
Policy Framework Specification Descreve como adicionar requisitos não-funcionais para serviços, como a segurança do defination serviço. Dois tipos de políticas existem: as políticas de interação e implementação. Políticas de interação afetam o contrato entre um solicitante de serviço e um fornecedor de serviços. Exemplos dessas políticas são a proteção da mensagem, autenticação e mensagens confiável. Implementação de políticas afetam o contrato entre um componente e seu recipiente. Exemplos de tais políticas são de autorização e estratégias de transação.
Binding Specifications Define a forma como os componentes são disponibilizados independente do código de programação. Essas especificações definem como os serviços e referências utilizam o tipo de transportes. Tipos de binding pode ser configurado para ambos os sistemas externos e internos entre os componentes.
Deployment and the Service Component Architecture Composição SCA são implantados dentro de um domínio SCA (Definição e configuração para um domínio-host).

Quando devidamente implementado, SCA pode ajudar uma empresa a minimizar a carga de trabalho em seus desenvolvedores, encurtar as curvas de aprendizagem, facilitar a reutilização de serviços e políticas de atualização sem ter que modificar a programação. SCA também facilita o controle sobre os métodos de acesso e a implementação de restrições relacionadas à segurança. Em vez de web services, a SCA permite que você conecte e componha web services através de RPC ou quaisquer outro meio.

WCF e SCA – Similaridade
Dado que o endereço WCF e SCA tem muito dos mesmos problemas, não é de se estranhar que as duas tecnologias são parecidas em inúmeras coisas. As semelhanças incluem:

Ambos fornecem uma maneira de usar objetos comuns para implementar aplicações orientadas a serviços. Em vez de exigir uma classe para herdar de uma classe base padrão, como no .NET Enterprise Services, ou implementar uma interface padrão, como no Enterprise J2EE JavaBeans (EJB), WCF e SCA simplificam o desenvolvimento permitindo que qualquer classe possa expor serviços. Ambos também permitem que os desenvolvedores se concentrem em escrever a lógica de negócio, proporcionando uma clara separação entre os meios de comunicações e lógica de negócio.

Ambos dependem de extras (atributos em. NET, anotações em Java) para permitir aos desenvolvedores definir interfaces de serviço. No WCF, por exemplo, uma interface C# que implementa um serviço é precedida pelo atributo [ServiceContract], enquanto na SCA, a mesma coisa é feita por uma interface Java, precedendo-a com a anotação @Service. Em ambos os casos, um WSDL pode ser automaticamente criado a partir da definição de interface.

Ambos usam a noção de bindings para empacotar diversas informações necessárias para se comunicar com um serviço, incluindo a forma de confiabilidade na comunicação, quais opções de segurança são utilizadas e etc. WCF e SCA também permitem bindings para especificar o protocolo SOAP e outros protocolos de web-services, permitentindo demais protocolos nativos simultaneamente. Cada tecnologia também define um formato baseado em arquivo XML de configuração para especificar esses bindings e fornece alguns bindings pré-definidos que os desenvolvedores podem customizar conforme a necessidade, totalmente extensível. Ambos dependem de as especificações WS-*, incluindo WS-Security e outros, para fornecer comunicação segura, confiável, e transacional usando SOAP. Ambos também expõe o comportamento de um binding usando WS-Policy. Esta abordagem comum nos dá boas razões para esperar uma efectiva interoperabilidade entre aplicações WCF e aplicativos SCA.

Ambos permitem tradicionais chamadas síncronas no estilo RPC, e ambos também suportam chamadas one-way e interações duplex (contratos duplex em WCF, interfaces de callback em SCA). Ambas as tecnologias também suportam sessão orientada comunicação, com o estado mantido ao longo de uma série de chamadas. Com WCF defini-se o atributo [(Session=true)ServiceContract], enquanto um SCA em Java especifica este comportamento para um serviço com a anotação @Scope(“session”).

Há muitas semelhanças mais. Ambos definem protocolos proprietários otimizados, bem como apoio explícito para binding REST. Estas duas tecnologias claramente tem objetivos solucionar problemas de criação de aplicações orientadas a serviços, e também é claro que seguem uma maneira similar de resolver os problemas.

Para aqueles que querem um mapo claro entre os conceitos acima e WCF:
Serviços – C# interface com declarações do WCF.
Componentes – Classes C# com declarações do WCF.
Composites – Section Service nos arquivos app.config e web.config.
Domínio – Host de serviço (IIS, WAS, Windows Process ou Windows Application).

O desafio da complexidade
SCA oferece uma nova base para a criação de aplicações orientadas a serviços. Em vez de substituir as tecnologias já existentes, como EJB, o objetivo declarado é o de trabalhar em conjunto com essas tecnologias. Uma vez que a SCA está disponível, um desenvolvedor Java precisa escolher quando usá-lo, em vez de, EJB, JAX-RPC (agora chamado de JAX-WS), ou outra coisa. SCA pode trabalhar em conjunto com estas tecnologias anteriores, os desenvolvedores devem decidir também quando combiná-lo com EJB ou outras abordagens. Enquanto SCA poderá um dia fornecer uma base sólida para a criação de aplicações orientadas a serviços.

Com o WCF, o objetivo principal foi o de simplificar a vida dos desenvolvedores. Em vez de adicionar uma outra tecnologia para a construção de aplicações orientadas a serviços, a Microsoft deu um passo bastante radical de expandir várias tecnologias existentes, incluindo Enterprise Services, .NET Remoting, ASMX, e mais, na abordagem única fornecida pelo WCF. Ao colocar uma aposta tão grande em serviços, a Microsoft apostou que aplicações orientadas a serviços realmente seria o padrão. Esta aposta foi correta, e o retorno será uma plataforma mais simples de desenvolvimento.

Satellite Assembly

Não é tão recente a necessidade de preparar nossas aplicações para internacionalização. A web quebra o conceito do in loco para um consumo de aplicações muitas vezes globalizado. Mas não adianta fornecer uma aplicação web, tal como um e-commerce para um mercado mundial, quando sua aplicação não atende as culturas estrangeiras. Precisamos preparar nossas soluções para se ajustar a idiomas, formato de data, fuso-horário, moeda e etc. Ajustar um aplicação não globalizada geralmente é um trabalho árduo, preparar uma aplicação para internacionalização desde o início aumenta o esforço por cuidados adicionais, como por exemplo traduções, mas ao longo do desenvolvimento as cautelas com a globalização torna-se uma rotina automática.

Nesse post quero falar de Satellite Assembly, recurso da plataforma .NET Framework que permite o desenvolvimento de aplicação com ajuste de idioma (multilingual), feature existente desde o início da plataforma e questão de prova de as primeiras certificação. Utilizando satellite assemblies você pode localizar recursos para diferentes idiomas em diferentes assemblies, carregando na memória somente o assembly correto de acordo com o idioma selecionado pelo usuário ou através de detecção automática pelo idioma e configuração regional do sistema operacional.

Por definição, satellite assembly não contêm código, exceto o que é gerado automaticamente. Por isso, eles não podem ser executados uma vez que não estão no assembly main. No entanto, note que os satellite assemblies estão associados a assembly principal, que geralmente contém recursos padrão Neutral (por exemplo, en-US) na propriedade do projeto. A grande vantagem é que você pode adicionar suporte para uma nova cultura ou substituir assemblies sem recompilar ou substituir do assembly principal do aplicativo (desde que preparou o idioma que necessita desde a início). Arquivos de recursos têm a extensão .resx e são armazenados em pastas correspondentes a cultura. No momento em que o aplicativo é compilado os resources são criados, gerando arquivos binários representantes dos arquivos .resx.

Para demonstrar como gerar satellite assemblies, criei um projeto Windows Forms e um projeto Class Library, no qual o aplicativo Windows Forms será o consumidor de uma biblioteca de “negócio” devidamente globalizada. No projeto Class Library criei uma pasta manualmente chamada Resource, adicionei 02 itens do tipo Resource File, um com nome Word.en-US.resx e outro Word.pt-BR.resx. Clique na imagem abaixo para uma melhor visualização.

SatelliteAssemblyResource

Adicionei um classe denominada Person e um método GetName para recuperar um texto a partir do resource. A classe ResourceManager se encarrega de ler e recuperar dados nos resources, note que informe o nome do resource e solicito o texto a partir da chave existente no resource sem especificar o idioma. A partir da cultura definida na thread da aplicação o ResourceManager se encarregará de pegar o texto no idioma correto. Poderia ter definido a cultura no App.config ou dar a opção para ou usuário escolher uma cultura.

SatelliteAssemblyImplements

Por fim, adicionei a referencia do projeto Class Library no Windows Application e fiz o consumo para exibir a mensagem de acordo com cultura (pt-BR) padrão do meu sistema operacional.

SatelliteAssemblyClient

Após a compilação da solução, no diretório Bin\Debug junto aos assemblies da aplicação estarão os diretórios dos idiomas existentes. Dentro de cada pasta de idioma estará um assembly chamado SatelliteAssemblyClassLibrary.resources.dll, nesse caso.
SatelliteAssemblyFolder

Para aplicações ASP.NET o processo é bem semelhante, os assemblies não são gerados individualmente como nesse exemplo e como padrão disponibiliza uma pasta dentro do projetos web chamada App_LocalResource, para que os resources (resx) permaneçam dentro dela e o pela configuração da cultura nas páginas ou web.config o framework se encarregue de traduzí-las. Na publicação das aplicações web, os resources são distribuídos juntos como um conteúdo na sua forma natural, arquivo .resx que pode ser editado. Uma recomendação importante é a definição do idioma padrão no assembly, nas propriedades do projeto, aba Application/Assembly Information…/Neutral Language defina o idioma padrão para que a aplicação saiba por qual idioma iniciar. De uma forma simples, podemos delegar ao framework para estruturar e recuperar dados para atender as altas exigências do mercado cada vez mais conectado e globalizado. Considerem essa prática e preparem suas soluções para crescimentos, expansões e novos mercados!