segunda-feira, 3 de fevereiro de 2014

Camadas x Pacotes



"Dividir para conquistar", esta é a maneira pela qual os desenvolvedores modernos lidam com a complexidade. Os softwares são divididos em diversas partes, seguindo várias tendências e metodologias (design patterns, 3-tier etc), e estas partes são interconectadas para formar o todo.

Recentemente, recebi um questionamento muito interessante, vindo de um ex-aluno meu: Camadas e Pacotes são sinônimos? Então, resolvi "fuçar" um pouco o assunto e tentar dar minha contribuição para esclarecer as coisas.



Pacote

O primeiro conceito que devemos examinar é o de "pacote". Vamos tentar abstrair a linguagem de programação, e tentar entender o que seria um "pacote"? Temos várias definições de "pacote de software", que se complementam ou suplementam: 
  • Pacote de software (Mundo linux): Um pacote de software é software que foi compilado a partir do código fonte, e preparado com um software Gerenciador de Pacotes (). Um pacote possui meta dados, como: descrição, versão e "dependências", permitindo que o Gerenciador de Pacotes possa mantê-lo sempre atualizado;
  • Pacote (UML): Um pacote na UML é utilizado para agrupar elementos, fornecendo um "namespace" (classificador de nomes) para eles. Um pacote pode conter outros pacotes, formando uma hierarquia destes;
  • Pacote (Programação modular): Permitem separar e agrupar a funcionalidade de um programa, em módulos independentes e intercambiáveis, tal que cada um contém tudo o que é necessário para executar determinado aspecto da funcionalidade do programa;
E, quando partimos para o mundo da implementação, seja em Java (TM) ou .NET (TM), vemos o conceito se confundir com o de pacotes executáveis.

No mundo .NET, agrupamos elementos (por exemplo, Classes), em pacotes executáveis chamados "Assemblies", que podem ser distribuídos como arquivos DLL ou EXE. E organizamos nossas classes em "Namespaces", permitindo que sejam identificadas através deles. Vale dizer que não há um relacionamento 1 para 1 entre "Assemblies" e "Namespaces". Um pacote pode ser entendido como um "Assembly" (ponto de vista físico) ou como um "Namespace" (ponto de vista lógico).

Já, no mundo Java, os conceitos de "Pacote" e "Namespace" se fundem. Um "Pacote" Java é uma organização física e lógica de classes, geralmente, distribuído como um arquivo compactado JAR / WAR / EAR (não é necessariamente obrigatório). Neste caso, um "Pacote" físico e lógico é a mesma coisa, identificado por um "nome de pacote Java".

O conceito que mais se aproxima da implementação é o da UML. Se o utilizarmos, estaremos promovendo uma ligação saudável entre Arquitetura, Projeto e Implementação do software. Então, combinando as definições de "Pacote UML" com "Pacote de Programação Modular", podemos chegar a uma definição satisfatória e independente da linguagem de programação:
Um "pacote" é um agrupamento de elementos (Classes, Interfaces e outros), inter-relacionados, sob um mesmo "Namespace", de tal forma que possam executar, de maneira independente, determinado aspecto da funcionalidade geral do software.

Características dos pacotes

Em todas estas definições, "pinçamos" algumas características fundamentais de pacotes:

  1. Nomenclatura: Todas as classes são agrupadas em um "namespace";
  2. Responsabilidade: O pacote atende a um determinado aspecto da funcionalidade do software;
  3. Completeza: Todos os elementos necessários para executar este aspecto da funcionalidade, estão presentes no pacote;

Quando temos pacotes bem feitos, eles apresentam estas três características, o que os torna "intercambiáveis", desde que as interfaces entre os pacotes sejam respeitadas.

Vamos imaginar um "pacote" que seja responsável pela persistência de dados. Se ele obedecer à especificação das interfaces (abstrações) de acesso à dados, então podemos substituir um pacote que usa Bancos de Dados Relacionais, por outro, que serialize os dados em arquivos XML, sem problema algum para o software.

Quando um pacote executa mais de uma responsabilidade, ou quando não está completo, tal flexibilidade ficará comprometida, exigindo esforço muito maior para ser alcançada.

Na verdade, estas características estão espelhadas em alguns "princípios" de projeto orientado a objetos:

  • Common Reuse Principle (Princípio do Reuso Comum): As classes em um pacote são todas reutilizadas juntas. Se você reusa uma classe no pacote, reusará todas elas;
  • Common Closure Principle (Princípio do Fechamento Comum): Se uma classe, pertencente a um pacote, necessitar ser alterada, então todas as outras classes do pacote, provavelmente, também o serão;
  • Release Reuse Equivalence Principle (Princípio da Equivalência entre Liberação e Reuso): A granularidade do reuso é a granularidade da liberação, como sempre liberamos as classes dentro de Pacotes, a menor unidade de reuso é o próprio Pacote;
Pacotes podem ter categoria funcional identificada através de Estereótipos, que devem também serem refletidos no próprio nome do pacote. Por exemplo "<<UI>>" seria um estereótipo que indica que o pacote ou componente está relacionado com a interface de usuário, enquanto o estereótipo "<<Persistence>>" indicaria que o pacote é voltado para a persistência de dados. 

Camadas

Bem, quando falamos em Camadas, esta é uma boa definição  (Wikipedia):   
Uma camada é um grupo de classes que possuem as mesmas dependências para outros módulos, formando um grupo de componentes reusáveis nas mesmas circunstâncias. Camadas são frequentemente pensadas em hierarquias, com relacionamentos de dependências entre si. 


Teoricamente, uma camada é um pacote com uma "ordenação" hierárquica, certo? Bem, podemos pensar dessa maneira... Só que, pela definição, nos parece que "Camada" é algo muito mais fortemente ligado a "Hierarquia", que pode incluir também dependências entre pacotes distintos. Logo, pensar em "Camada" como algo maior que apenas um "Pacote", nos parece razoável. 

Também existe uma confusão muito grande entre "Camada" e "Tier" ou "Nó" (Computacional). Tudo isto é advindo da própria origem do termo "Camada", que nos remete à antiga arquitetura "Cliente / Servidor", criada para desacoplar o mecanismo de seleção de dados do mecanismo de recuperação de dados. 

Nos antigos sistemas baseados em "xBASE", quando um usuário fazia uma seleção e/ou projeção de dados, era necessário ler todos os índices, pois o software era "Monolítico" e instalado em uma máquina de usuário, em uma Rede Local. Como era comum deixar o Banco de Dados em um Servidor de arquivos, em máquina separada, todos os arquivos envolvidos na pesquisa do usuário trafegavam pela Rede, aumentando a quantidade de colisões e diminuindo sua velocidade. 

Então, na arquitetura "Cliente / Servidor", o aspecto de persistência e recuperação de dados era dividido em duas "Camadas" de software, cada uma rodando em um "Nó" separado da Rede. O "Cliente" solicitava os dados ao "Servidor", que fazia a leitura local, retornando apenas o que foi solicitado.

3 Camadas

Uma arquitetura que se popularizou foi a famosa "3 Camadas", também conhecida como "3 Tier". Neste tipo de arquitetura, temos o software dividido em três camadas físicas, que rodam em computadores separados na Rede:
  • Apresentação: Executada na máquina do Usuário, contém todo o código necessário para comunicação com ele, desde a Interface de Usuário até a lógica de validação de dados e de comunicação com o Servidor;
  • Negócio: Executada em um Servidor de Aplicação, contém toda a lógica para validar os pedidos dos usuários, invocar as regras de negócio necessárias e formatar as respostas a serem enviadas para as estações Clientes;
  • Persistência: Executada em um Servidor de Banco de dados (DBMS), é responsável por executar consultas e atualizações SQL, mantendo a integridade referencial e relacional dos dados.

Camadas e Pacotes

Diante desta discussão, vemos que Camadas e Pacotes são formas distintas de agrupar e organizar classes, sendo que as principais diferenças são:

Camada Pacote

Ligada à instalação

Ligado à componentização

Organização lógica

Organização física

Contém pacotes

Contém Classes

É claro que a Literatura ainda mistura muito as coisas, logo, frequentemente vemos referências à Camadas e Pacotes como se fossem sinônimos, o que não é um erro em si, mas pode levar a interpretações equivocadas.