Em posts anteriores que remontam a 4 anos atrás falei sobre como as pessoas confundem o padrão MVC com o conceito de Separação de Camadas e o padrão ECB. E embora esta confusão ainda continue (como mostras posts recentes no GUJ) me parece que sua raízes se prendem a conceitos de arquitetura e algumas convenções.
Pacotes
Começando pelo básico temo conceito de pacote. O conceito de pacote em java nada mais é que uma pasta onde você coloca os arquivos .java. Mas o caminho desta pasta juntamente com o da classe do arquivo forma o nome completamente qualificado da classe ( que vc pode acessar em código usando o método getName() do objeto Class). O caminho completamente qualificado é importante para destinguir as classes que têm o mesmo nome ‘simples’. Por exemplo, na API Java SE temos duas classes List. A interface da API de collections, e a implementação da API de AWT. Sem o nome do pacote estas classes não poderiam existir com o mesmo nome. Com o nome do pacote temos java.util.List e java.awt.List e não ha confusão nenhuma.
Então suas classes também devem existir dentro de pacotes. Primeiro para que não haja problema se você criar duas classes com o mesmo nome, segundo para que não haja problema se sua classe tem o mesmo nome de outra classe de outra API. Então, por convenção, quando se vai criar uma API nova para um projeto ou para um sistema, criamos um pacote base onde iremos criar todos os outros pacotes. Para garantir que os nomes não se repetem, precisamos de um órgão mundial que ateste que os nomes são únicos. Isso não existe, mas existe algo semelhante : o registro de domínios da internet. Então, por convenção se usa o domínio invertido da empresa ou do site do projeto. Um exemplo de domínio invertido seria com.javabuilding se o site fosse www.javabuilding.com.
Dentro do pacote base você irá criar suas classes. À medida que as classes são definidas, você vai querer organizar em sub pacotes para fica mais fácil de encontrar essas classes depois. Pacotes comuns são util (para classes utilitárias), domain (para classes com regras de dominio) , services ( para serviços) entre outros. Estes nomes não têm qualquer relação com as camadas ou a arquitetura do sistema mas sim com as responsabilidades das classes. É a responsabilidade da classe que tem ligação à arquitetura e não o nome do pacote. Então, às vezes o nome do pacote coincide com uma camada ( por exemplo, domain) , mas ás vezes não (utils, por exemplo). Em tese vc pode deixar todas as classes no pacote base e está tudo certo. apenas vai ser mais complicado de gerenciar, mas quando tem uma duzia de classes está de bom tamanho usar só o pacote base.
Módulos
Módulo é um conceito que não existe diretamente em java (e existe a JSR 294 para o incluir), mas informalmente módulos são agrupamentos físicos de classes, ou seja, são arquivos .jar. Mas é um pouco mais que isso. Quando você cria uma classe , você cria um agrupamento de elementos (atributos e métodos) e você define como eles são visíveis a outra classes ( usando os modificadores de acesso, public, private e protected), mas tudo o que for publico será visível a todo o mundo. Às vezes você não quer isso. Às vezes você cria subpacotes com detalhes do pacote principal e as classes que ali estão deveriam apenas ser visíveis ao pacote pai. contudo, para o java não existe o conceito de pacote pai, e é aqui que o pacote se diferencia de uma pasta. A JSR 294 tentar dar mais ferramentas para definir o acesso entre pacotes de forma mais fiel ao conceito que usamos na prática.
Por enquanto, sempre que você coloca suas classes no arquivo jar , todas as que são publicas, são utilizáveis por qualquer um.
Existem dois tipos especiais de jar, o war e o ear que são empacotamentos especiais conforme os padrões da JEE. Além das classes estes arquivos jar contêm arquivos xml ou outros, que definem propriedades do arquivo como um todo e servem para serem interpretados pelos containers (de web e ejb respectivamente). A especificação JEE moderna é mais flexível em relação a como empacotar suas classes nestes arquivos, mas em certas circunstancias ( como o uso de tecnologia RMI, por exemplo) torna necessário uma divisão judiciosa de cada pacote dentro de cada arquivo jar. Dentro de cada modulo.
Camadas e Andares
O conceito de camada (Layer) nada mais é que o conceito abstrato que classes que trabalham juntas devem estar juntas. Mas este “estar juntas” significa “trabalharem juntas” e não “estarem fisicamente no mesmo lugar”. Por isso, um camada pode compreender classes de mais de um pacote. Esta agregação em camadas é abstrata e é a visão da arquitetura sobre como é a estrutura do sistema em run time. É apenas um conceito. Porque fisicamente as classes estão todas juntas na memoria da JVM a todo o momento.
A separação em camadas é conceptual e não se traduz na separação de pacotes ou em uma organização especial dos pacotes.
O conceito de Andar (Tier) é mais físico que o conceito de camada e não têm apenas que ver com a responsabilidade das classes, mas com o seu papel no fluxo da informação. Uma estrutura importante compreender antes é o conceito de nodo. O Nodo é uma normalmente uma JVM. Uma máquina física pode ter mais do que um nodo e pode ter mais do que uma instancia do mesmo nodo. O conceito de nodo é especialmente importante em aplicações distribuídas. Em aplicações web temos o browser e o servidor. O Browser é um nodo, e o Servidor é outro nodo, para ser mais exato, Browser é uma instancia de um nodo do tipo Brwoser, e Servidor é uma instancia de um nodo do tipo Servidor. O usuário pode abrir mais que um browser e a empresa pode criar mais do que um servidor.
Dentro de um nodo ha um processo de entrada e saída e entrada de informação. Normalmente o nodo tem acesso a algum tipo de informação ou serviço que alguém quer usar. Então para estruturar como o nodo vai processar a informação, o nodo é dividido em Andares. Andares definem responsabilidades do nodo. Cada andar será implementado usando um conjunto de classes. Estas classes serão organizadas em camadas. Um nodo têm mais que um andar e cada andar pode ter mais do que uma camada e as classes de cada camada serem agrupadas em um ou mais do que um pacote. Alguns pacotes podem ser usados em mais do que uma camada ( é normal por exemplo usar o pacote de entidades em todas as camadas) contudo as camadas em si, e os andares seguem uma regra de cadeia onde o andar ou camada de cima apenas comunica com o andar o camada de baixo. Isto significa que existiram camadas nas fronteira dos andares e possivelmente com “uma metade de um lado e a outra metade do outro”. Isto é típico quando se usa comunicação remota em que o cliente envia ao servidor alguma informação as classes que estão no cliente enviando os dados e as que estão no servidor lendo esses dados fazem parte da mesma camada (porque fazem parte da mesma responsabilidade de comunicação), mas de andares diferentes, porque estão em nodos diferentes.
O padrão atual é usar 5 andares : Cliente, Apresentação, Domínio, Integração e Recursos. Em um sistema desktop standalone (por exemplo o word) o Cliente é a parte gráfica com que o ser humano interage, ou o protocolo com que outro sistema interage. O cliente interpreta o input do usuário (seja humano interpretando movimento do mouse e teclas) e cria um output (normalmente gráfico). Os inputs são intepretados como gestos. Por exemplo, apertar com o mouse o botão “salvar” significa o gesto “salvar” e apertar com o mouse o botão “sair” significa o gesto “sair”. Contudo utilizar teclas de atalho podem significar o mesmo gesto. E no caso do “sair” , simplesmente fechar a aplicação no OS representa o mesmo gesto. O conceito de Gesto é portanto mais direcionado à intenção do usuário e não a como ele expressou essa intenção. A Apresentação é o andar ondes os gestos são traduzidos em ações. Estas ações podem ser ações que alteram o estado do sistema (persistem algo), que comunicam com outros sistemas ou que simplesmente descrevem como será o próximo output do andar Cliente. O andar de domínio contém as regras de negocio. As entidades, as relações entre elas e o que acontece quando uma entidade é criada, destruída, associada a outra , etc.. O andar de Integração permite que o nodo se comunique com outro nodos para obter informações ou requisitar a invocação de serviços e o andar de Recursos são esses serviços ou informações em si. Arquivos, Bancos de dados, Serviços de Dados são recursos de uso comum.
A cada andar , corresponde uma ou mais camadas. Poderíamos por exemplo usar swing para construir nosso andar cliente. O swing permite que sejamos avisados quando o usuário aperta um botão ou uma tecla. Mas criar uma tela swing implica no uso de muitos objetos e composição de uns nos outros. Mas a resposta ao gesto do usuário não precisa dessa complexidade. Então provavelmente iremos criar um pacote com as classes de tela criadas usando Swing, e um pacote com as classes que têm os métodos que representam os gestos do usuário. E umas serão ligadas às outras. Quando o usuário apertar um botão ou digitar um texto a classe no outro pacote será alertada, ela decidirá o que fazer. às vezes é apenas mostrar outra tela ou pintar algo com alguma cor. Ou seja, lógica de navegação interna ao próprio cliente. Outras vezes é a invocação de um gesto como “salvar” ou “validar” ou “calcular” e ai temos que chamar o andar de apresentação.No andar de Domínio termos nossos serviços, entidades, validadores e outras classes que contenham regras. Poderíamos colocar tudo num pacote só, mas possivelmente é mais agradável criar um pacote entities para as entidades, services para os serviços, validators para os validadores e assim vai.
Existe uma correspondência entre um conjunto de pacotes e uma camada, e entre um conjunto de camadas e um andar, e nada impede de termos uma relação um-para-um-para-um, mas simplesmente quando maior for o sistema, quantas mais classes precisar, colocar tudo num pacote só, mesmo que na mesma camada, começa a ficar confuso.
Não existe uma nomenclatura especial para os pacotes, excepto a convenção para o pacote base ser o domínio invertido de algum site relacionado à API ou à empresa. Conforme a arquitetura que você usar , podem existir mais ou menos nodos, e em cada nodo mais ou menos das camadas que referi ( por exemplo, no nodo Browser raramente você usa o andar de Recursos, mas ele está lá quando você tem um upload na sua página ou usa o novo recurso de webstore do html5. Mas são são todas as aplicações que têm isso.)
Eu sei que não é todos os dias que você tem que pensar no conceito de nodo e andar e especialmente não se você está mais interessado e criar código. Mas ao falarmos de pacotes, camadas e andares não estamos mais pensando em código, estamos pensando em organização. E se você está pensando em organização, você tem que conhecer estes conceitos. Caso contrário é como tentar arrumar sua casa sem conhecer o conceito de caixa, e saber que existem vários tipos de caixa e que umas encaixam nas outras.
Ótimo artigo Sergio. Gostaria de fazer uma pergunta.
Você diz
Eu concordo, mas uma coisa que tenho tentado fazer é “transformar” minhas camadas em pacotes. Assim, associo o conceito com uma organização física. Faço isso porque antigamente eu organizava os pacotes conforme a “lógica” da operação que eles executavam. Um exemplo. Uma classe de dominio Cliente ficava no pacote dominio.cadastros, porém muitas vezes essa classe era usada somente em operações do financeiro por exemplo. A meu ver não fazia muito sentido deixar a classe no pacote de cadastros e acabava retirando e o colocando-a mais próxima da onde seria usada e isso fez com que esse tipo de pacote acaba-se sumindo das minhas aplicações! Então faço a pergunta, não seria melhor tentar aplicar aos pacotes o conceito de camadas na aplicação? Claro que uma camada continuaria e poderia usar mais de um pacote, mas tenho começado a transformar meus pacotes em “camadas” e tenho gostado do resultados, o que você acha disso?
Se eu bem entendi vocês está removendo os subpacotes por exemplo “dominio.cadastros” e ficando apenas com o pacote “dominio”. Numa primeira perspetiva isso seria porque o nome “cadastro” é horrível (não é uma boa abstração) e a experiencia lhe mostrou isso. Se vc usasse “dominio.financeiro” em vez, provavelmente não teria removido o subpacote. Não ficou claro o que vc quer dizer com “mais próxima de onde seria usada”. Por outro lado, se vc simplesmente tem os pacotes “apresentação”, “dominio”, “integração” isso significa que os pacotes refletem andares, e não camadas. E os seus subpacotes tlv reflitam camadas desses andares como por exemplo “dominio.entidades” e “dominio.servicos” e “dominio.repositorios” e “dominio.validadores”.
Como disse no texto dá para fazer de muitas formas. Eu pessoalmente use os andares para o nome do pacote “macro” ( o que fica logo abaixo do pacote base) e depois lá dentro vou pondo conforme é prático. Normalmente pela responsabilidade , tipo todos os presenters em um pacote, todos os serviços, todos os repositorios, etc.. às vezes têm que ser assim porque uso funcionalidade de discovery (autowire do spring) e quero que ele veja um pacote especifico e não misture as classes. Outras vezes não. Mas sempre ha o pacote utils da vida que não é um andar. Por isso que eu falo que é difícil um para um, mas pelo menos ha alguma coerência.
Se transformar os pacotes em camadas é bom para você e não ha violação de nenhuma regra ( por exemplo, circularidade de dependência ) ótimo. Vá em frente. Não ha uma receita de bolo para o jeito certo. Apenas sinais para os jeitos errados. E o jeito é errado quando viola os conceitos. Como disse é uma questão de caixas. E tudo depende de como as organiza.
Humm.. não é bem isso, vamos definir alguns padrões para ficar mais claro.
Bom digamos que, como você colocou, os pacotes principais sejam os andares. Então meus sub pacotes podem ser minhas camadas!
Só, que, como tu colocaste, a camada é uma abstração lógica. Então, você separa as classes em dominio.serviços, dominio.entidades. Nesse contexto, você esta classificando as classes conforme o que elas “represetam”, conforme as operações que realizam. Assim, digamos que eu tenha uma entidade chamada Venda e essa entidade utiliza alguns serviços. Os seviços e a classe Venda se relacionam entre si, mas ficam em pacotes separados. Já no pacote de serviços, posso ter diversas classes que nem se relacionam entre si! Sendo assim, o que quero colocar é:
Por que não colocar as classes em pacotes mais significativos e onde fiquem perto das classes que são utilizadas?
Exemplo. Eu posso retirar a entidade Venda e coloca-la em um pacote chamado dominio.departamentoDeVendas. Ali também coloco os serviços que são utilizados pela Classe Venda e quaisquer outras classes do “dominio” que estajam relacionados com o departamentoDeVendas. Assim, ao invés de agrupar as classes em pacotes, conforme o que elas “representam” (entidades, serviços) você as organiza conforme suas relações. Assim, classes que estão relacionadas ficam dentro do mesmo pacote e o pacote acaba se transformando em uma “Camada”, ou seja o conceito praticamente deixa de ser abstrato.
Entendi o seu ponto. Realmente nunca pensei nisso. Suponho que seja uma opção válida. O problema ai é acesso entre as classes. Por exemplo, a interface do serviço X ficar no mesmo pacote que a implementação não tem problema (à priori) pois vc pode isolar no modulo ( no jar) , mas fica mais complicado programar o ant ou o maven para fazerem a separação. É só isso que vejo – a facilidade de modularizar (colocar nos jars certos as classes certas) – mas suponho que se poderia resolver com um subpacote, ou um regex de algum tipo, ou mesmo explicitando as classes na não. E claro que em deploy onde não é preciso vários módulos isso fica simples (war , por exemplo). Teria que pensar melhor para saber se isso não viola regras de acesso. Referencia circular, por exemplo. Não acontece do pacote X precisar do Y que precisa do X ? Por outro lado, estando tudo no mesmo pacote fica mais fácil usar acessos especiais e diminuir as coisas publicas.
É interessante. Vou explorar isso na próxima oportunidade. De certa forma, realmente parece mais vantajoso.
Nunca tive problemas, mas também nunca desenvolvi nada muito complexo para ter problemas. Esse estalo eu tive quando estava lendo o DDD do Eric Evans, tem uma parte que ele fala exatamente sobre isso. Quanto a diminuir a visibilidade de métodos públicos é fato. Isso já me ajudou muito na realização de testes, as vezes tem algo que eu quero testar, com alguma lógica sacana, mas que não faz sentido ter acesso público ou nem pode te-lo, assim utilizo visibilidade de pacote para essa lógica e posso acessá-la tranquilamente de outra classe do mesmo pacote para os testes.
bom dia
Qual a diferença de se ter as 3 camadas em um único servidor ou te-las em servidores diferentes?
A diferença é que não é possível ter 3 camadas em servidores diferentes.
Camadas são separações no código de um único nodo (servidor).
Mesmo que você tenha um servidor web para a ui um servidor de aplicação e um servidor de dados (aka banco de dados) em cada um destes ainda terá 3 camadas em cada um.
Leia https://sergiotaborda.codedesign.world/2012/02/arquitetura-ecb para ver se ajuda a esclarecer.