Arquitetura Padr√£o Completa 

Feb/13
23

Hoje em dia, a maior parte das empresas de software produz sistemas com uma arquitetura muito semelhante. Sobretudo no mundo java.¬† Isto n√£o significa que seja uma boa arquitetura. Apenas √© a mais comum. E √© a mais comum porque acabou virando um meme e as pessoas simplesmente aceitam que seja assim.¬† Ent√£o, hoje o objetivo aqui √© descrever qual √© esta arquitetura e como ele deveria realmente ser para se tornar ainda melhor. Analisaremos tamb√©m porque essa “ultima milha” n√£o √© normalmente feita.

A arquitetura base √© a arquitetura client-servidor¬† utilizando HTTP como protocolo.¬† N nodos cliente K nodos servidor de aplica√ß√£o e H nodos de servidor de banco de dados . Normalmente K = H = 1. N√£o estou contando aqui com cloud, porque isso ainda n√£o √© comum. Nem solu√ß√Ķes de balancing porque isso √© especifico de cada deploy. No nodo cliente temos um navegador HTTP que interpreta HTML. No nodo Servidor de Aplica√ß√£o temos a aplica√ß√£o em si e com quem o cliente se conectar e comunica via HTTP. No nodo de Servidor de Dados temos algum Sistema Gerenciador de Banco de Dados (SGBD).

arquitetura3nodos

Embora o usu√°rio interaja com navegador no nodo cliente, o b√°sico desta arquitetura √© alicer√ßado no funcionamento padr√£o do Navegador e dos protocolos HTTP e HTML. Ao informar uma URL ao navegador ele ir√° incorrer em uma s√©rie de mecanismos de localiza√ß√£o que ir√£o por fim coloc√°-lo em comunica√ß√£o com o Servidor de Aplica√ß√£o. Nesse ponto a requisi√ß√£o HTTP ser√° interpretada e uma resposta ser√° formulada e devolvida ao navegador. Portanto, em uma arquitetura web a l√≥gica que governa o cliente √© dividida em duas partes. Uma parte que vive fisicamente no nodo cliente e √© puxada via rede pelo cliente , do servidor, e interpretada no cliente.¬† Esta informa√ß√£o √© chamada comumente de “p√°gina”. A p√°gina pode ser associada um outro conjunto de URLs que precisam ser obtidos simultaneamente para que a p√°gina possa ser processada e mostrada ao usu√°rio. Por exemplo, imagens e estilos CSS. Al√©m disso temos tamb√©m scripts em javascript que permitem controlar e comandar o navegador, especialmente para responder a gestos do usu√°rio como sele√ß√Ķes, cliques, drag, drop, e hoje em dia at√© toque.¬† O sistema ent√£o se utiliza desta customiza√ß√£o do navegador para entender os gestos do usu√°rio. O gesto do usu√°rio √© enviado para o servidor para interpreta√ß√£o. Isto pode ser feito por um comando POST ou GET e pode ser s√≠ncrono ou ass√≠ncrono (ajax). Temos ent√£o toda uma camada de c√≥digo que lida em criar a interface gr√°fica dentro do navegador e interpretar os gestos do usu√°rio tanto no navegador como no servidor. Este conjunto de c√≥digo ocupa p andar de Visualiza√ß√£o ( User Interface) e √© o primeiro andar no servidor. Ou seja, √© a primeira fatia do nodo servidor que comunica com o nodo cliente. Depois que a UI interpretou o gesto do usu√°rio sabemos que o usu√°rio deseja. Por exemplo “salvarOsDadosNaTela” ou “NavegarParaATelaAnterior” ou “FazerLogin” e por ai vai.

Em frameworks action based cada gesto √© uma a√ß√£o que √© enviada a um URL de processamento. Internamente esta a√ß√£o √© mapeada para um m√©todo em uma classe. O m√©todo √© onde o programador pode processar uma resposta e depois reenviar o resultado para a mesma URL que fez a chamada, ou outra. Em frameworks baseados em componentes , cada gesto √© um evento que algum objeto listenner¬† ir√° receber (padr√£o Observer). Esse listenner √© eventualmente um m√©todo onde o programador pode processar a resposta. Contudo em frameworks baseados em componentes n√£o ha o conceito de URL nem de “p√°gina”. Existe o conceito de um componente que representa a tela do navegador e o programador pode interagir com ela como se fosse a tela de um desktop. Todo o processamento do protocolo HTTP, HTML e Ajax √© feito pelo framework.

andares1e2

Exemplos de frameworks baseados em acção são o Struts 1 e 2,  o Spring MVC , o Grails, o Stripes , o Mentawai e  o Vraptor, Exemplos de frameworks baseados em componentes são o JSF , o Wicket, o Vaadin e o ZK.

Conceptualmente todos os gestos s√£o eventos e todos t√™m que cair em algum m√©todo que decidir√° o que fazer.¬† Tradicionalmente este m√©todo √© diretamente o m√©todo que o framework prov√™. N√£o ha nenhuma outra abstra√ß√£o. Este m√©todo imediatamente pode invocar a camada de servi√ßos e realizar tarefas relacionadas √† interface gr√°fica como pintar componentes, escond√™-los, desabilit√°-los , etc..¬† Em primeira aproxima√ß√£o isto parece facilitar o trabalho de constru√ß√£o, mas olhando melhor n√£o ha suficiente abstra√ß√£o. O que acontece quando diferentes m√©todos produzem o mesmo resultado ? Porque n√£o ha uma camada de interpreta√ß√£o dos gestos em si, e apenas dos comandos do protocolo, ha necessidade de replica√ß√£o de c√≥digo ou da cria√ß√£o de estruturas de hierarquias de classes para reaproveitar o c√≥digo e mant√™-lo o mais DRY (Don’t repreat yoursefl) poss√≠vel. Este √© um ponto onde as arquiteturas comuns falham. E esta falha custa milhares de reais em todos os projetos e ao longo da vida do produto.¬† Falta uma camada de abstra√ß√£o entre o processamento “cru” dos eventos de visualiza√ß√£o e o processamento “alto n√≠vel” dos gestos do usu√°rio.¬† O exemplo cl√°ssico √© ter um bot√£o de “sair” no site. E ao apert√°-lo o usu√°rio perde a sess√£o de autentica√ß√£o (faz sign off, ou log out). Mas e se o usu√°rio simplesmente navegar para outro site ? Ou se ele simplesmente desligar o navegador ?¬† N√£o deveria o sistema executar a mesma a√ß√£o ? Sim, deveria. Mas a forma t√©cnica de saber se o usu√°rio tomou aquelas a√ß√Ķes √© diferente (normalmente passa por ouvir um evento no navegador com javascript e lan√ßar uma invoca√ß√£o via ajax, ou registrar um listener com web container para saber quando a sess√£o expirou) . Ent√£o o andar de Visualiza√ß√£o, pode fazer chegar v√°rios tipos de comandos originados de diferentes lugares, mas todos eles representam a mesma coisa. Ent√£o, do ponto de vista do andar seguinte – a Apresenta√ß√£o – eles comandos deveriam ser canalizados para os mesmos m√©todos nas mesmas classes do andar de apresenta√ß√£o.¬† Este andar √© composto por objetos que¬†nada sabem sobre como acontece o processo de comunica√ß√£o com o usu√°rio. Eles n√£o recebem objetos do frameworks subjacente nem do protocolo ( como HttpRequest ou HttpSession). Este andar √© agn√≥stico assim como todos os outros abaixo dele. Para chegar neste modelo agn√≥stico √© necess√°rio utilizar objetos de transporte entre o andar de cliente e o de apresenta√ß√£o. Padr√Ķes como o MVP s√£o muito uteis nesta camada permitindo que todos os comandos do usu√°rio sejam canalizados para os mesmos gestos no objeto Presenter. Existem v√°rios “sabores” do MVP. Uns em que a View √© mais ativa e outros onde √© mais passiva (ou seja, tem mais ou menos conhecimento sobre as regras dos objetos). Idealmente ela deve ser o mais passiva poss√≠vel, sem ser completamente est√ļpida. Ou seja, ela deve automatizar tudo o que for relacionado ao mecanismo do cliente , da visualiza√ß√£o em si e da coleta de comandos do usu√°rio, mas n√£o deve interferir com as regras do que deve acontecer. Isto porque, ao colocar as regras no andar de apresenta√ß√£o, ele poder√° ser reaproveitado no futuro mesmo que todo andar de cliente seja refeito, remodelado ou at√© que seja portado para outro tipo de interface gr√°fica ( Desktop ou Mobile, por exemplo). Isto n√£o √© muito dif√≠cil, ajuda a organizar o c√≥digo, ajuda a delimitar responsabilidades e a isolar os os servi√ßos ainda mais para um “core” do sistema que n√£o tem que se preocupar com o que o usu√°rio v√™.¬† A longo prazo √© um ponto de extens√£o e de flexibilidade. A longo prazo facilita a manuten√ß√£o e a modifica√ß√£o do sistema para atender outros tipos de UI e portanto diminui custos e protege o investimento inicial. Sites tendem a mudar constantemente de look and feel para sempre deixarem o usu√°rio com o conceito de “frescura” e de que o site n√£o est√° abandonado. Sites de e-comerce mudam frequentemente conforme a √©poca do ano ( natal , p√°scoa, carnaval, etc..) para atender melhor os consumidores.¬† Produtos comerciais que se utilizam da arquitetura web, mesmo que n√£o sejam usando na rede publica, mas apenas na rede da empresa, se beneficiam tamb√©m pois a usabilidade a experiencia do usu√°rio com o software dependem drasticamente da interface gr√°fica com ele.¬† O software pode fazer maravilhas internamente, mas se a interface gr√°fica for pobre, as pessoas simplesmente n√£o o usam.¬† A interface gr√°fica tem que ser ajustada constantemente para manter o usu√°rio interessado e utilizando o produto. √Č como a pasta de dentes. Originalmente o produto que realmente faz efeito √© cinza. mas quem quer lavar seus dentes com uma pasta cinza ? Ent√£o o produto √© colorido artificialmente para ficar branco. Apenas a est√©tica √© a diferen√ßa entre usar e n√£o usar. A est√©tica, a imagem, √© tamb√©m o maior ativo do produto e onde est√° o maior investimento e risco. Portanto uma arquitetura que permita ter v√°rias visualiza√ß√Ķes do mesmo beneficio subjacente e que permite comparar a experiencia do usu√°rio com uma e com outra, √© uma arquitetura mais econ√īmica.

O andar de Apresenta√ß√£o delega ao andar de Dom√≠nio para que este processe as l√≥gicas , as regras , em suma, o prop√≥sito do sistema. A forma como a Apresenta√ß√£o faz isso √© atrav√©s de servi√ßos (padr√£o Service), mais concretamente servi√ßos p√ļblicos (padr√£o Service Fa√ßade). A ideia dos servi√ßos p√ļblicos √© que eles podem ser usados por qualquer camada de apresenta√ß√£o e podem at√© ser feitos diferentes grupos de servi√ßos para diferentes tipo de apresenta√ß√£o. Contudo estes servi√ßos n√£o realizam nenhuma tarefa ou regra de dom√≠nio. Eles simplesmente orquestram chamadas aos verdadeiros servi√ßos do dom√≠nio e outros tipos de objetos que existem no dom√≠nio a fim de¬† realizar a tarefa prometida √† apresenta√ß√£o.¬† Ent√£o, por exemplo, um servi√ßo publico pode simplesmente ser uma casca para uma simples pesquisa no banco de dados, ou pode ser a porta de entrada de uma transa√ß√£o complexa onde v√°rias entidades s√£o necess√°ria e chamadas a entrevir. O uso de servi√ßos p√ļblicos permite uma grande flexibilidade porque podem reaproveitar vezes sem conta as l√≥gicas mais complexas contidas nos servi√ßos ou nas entidades de dom√≠nio.¬† Da mesma forma que uma Visualiza√ß√£o pode ser modificada, assim tamb√©m pode a Apresenta√ß√£o. Como regra geral um elemento disposto em fatias como os andares ,ou as camadas,¬† deve partir do principio que diferentes elementos podem ser colocados acima dele. Ent√£o n√£o se trata de poder satisfazer um contrato, mas v√°rios. Mas todos estes contratos t√™m o mesmo prop√≥sito que √© , de alguma forma, invocar e utilizar as capacidades da camada de dom√≠nio.

Mas o Dom√≠nio n√£o vive sozinho e muito frequentemente ele precisa da ajuda de outros servi√ßos, inclusive de outros sistemas, para completar seu prop√≥sito. Por outro lado, o Dom√≠nio encapsula o estado do sistema em uma forma n√£o trivial e √© necess√°rio que esse estado seja persistido de alguma forma, ou todo o esfor√ßo do usu√°rio que interage com o sistema seria f√ļtil e em v√£o. Para isso o Dom√≠nio faz uso de Servi√ßos de Integra√ß√£o. Assim como a Apresenta√ß√£o se utiliza dos contratos dos servi√ßos p√ļblicos fornecidos pelo Dom√≠nio, o Dom√≠nio se utiliza de contratos p√ļblicos de servi√ßos de outros sistemas. Os dois sistemas mais usuais s√£o o Servi√ßo de Persist√™ncia, j√° mencionado, e o Servi√ßo de Envio de E-mail. Mas outros servi√ßos podem ser necess√°rios conforme o prop√≥sito do sistema. Servi√ßos de Cart√£o de Cr√©dito. Servi√ßos Especializados de Roteamento (Mapas). Servi√ßos de Geo-Localiza√ß√£o. E tantos outros.¬† A necessidade do Servi√ßo de Persist√™ncia √© t√£o comum que nem pensamos muito nele como um servi√ßo, e mais como uma parte integrante de Dom√≠nio. Contudo isto √© um erro. √Č necess√°rio isolar o Modelo de Dominio que s√£o as entidades que formam o neg√≥cio (Clientes, Produtos, CarrinhoDeCompras, Estoque, etc..)¬† da sua representa√ß√£o persistente (padr√£o Memento). Hoje em dias com ferramenta como JPA e Hibernate esta fronteira √© muito dilu√≠da. Esta dilui√ß√£o √© vista como boa e produtiva durante a constru√ß√£o, mas √© muitas vezes na realidade um ponto fixo do sistema. O Dominio fica engessado e a seguir o modelo de dados do banco e vice-versa. Ha necessidade de desacoplar estes conceitos e para fazer isso foi sugerido um padr√£o chamado Domain Store. O Domain Store faz parte do Dom√≠nio no sentido que ele conhece e entende o modelo de entidades, mas √© capaz de persistir os dados dessas entidades em objetos em um modelo, possivelmente,¬† diferente. Existe ent√£o um mapeamento entre o modelo de dom√≠nio e o modelo de persist√™ncia. O Domain Store √© um dos Servi√ßos de Integra√ß√£o mais utilizados hoje. Contudo ainda √© muito fundamentado em ferramentas de mercado e poucos se atrevem a criar este servi√ßo de forma mais abstrata. Ent√£o, quando um sistema passa de utilizar um SGBD relacional e precisa usar um que n√£o √© relacional (NoSQL), ou at√© os dois em paralelo para guardar informa√ß√Ķes diferentes, a persist√™ncia simplesmente come√ßa a ser uma dor de cabe√ßa.

Se o Dominio √© onde est√£o contidas as regras que tanto trabalho deram para serem levantadas e extra√≠das dos stakeholders n√£o √© prudente desperdi√ß√°-las. Ent√£o, da mesma forma que podemos apresentar ao usu√°rios diferentes visualiza√ß√Ķes das mesmas regras, tamb√©m queremos utilizar servi√ßos externos e alternar entre eles, com a mesma facilidade. Tudo isto para proteger o investimento que foi feito na produ√ß√£o do dom√≠nio. Na produ√ß√£o do core (franc√™s para cora√ß√£o).¬† Temos portanto que o Dom√≠nio assenta em um andar de Integra√ß√£o.

A Integra√ß√£o √© um andar que prov√™ servi√ßos que n√£o s√£o particulares a um dom√≠nio, como persist√™ncia, sistema de arquivos, indexa√ß√£o ou e-mail, mas¬† que podem ser utilizados por muitos dom√≠nios. Estes servi√ßos s√£o normalmente desenhados de forma agn√≥stica do dom√≠nio e podem servir de fato como substrato para diferentes aplica√ß√Ķes, ou seja, diferentes produtos. Por isso estes servi√ßos s√£o muitas vezes chamados de “Servi√ßos de Infraestrutura” , mas √© preciso mencionar que os verdadeiros servi√ßos de infraestrutura s√£o aqueles providos pelos outros sistemas e o que temos no nosso servidor s√£o apenas formas de invocar esses servi√ßos, formas de integrar com eles. Por isso √© preciso distinguir entre Servi√ßo de Infraestrutura que √© realizado pelo outro sistema (por exemplo, um servidor de e-mail √© que realmente envia o e-mail e n√£o n√≥s, ou um SGBD √© que realmente persiste, e n√£o n√≥s)¬† e Servi√ßos de Integra√ß√£o que s√£o meras representa√ß√Ķes nossas de como queremos usar esses servi√ßos.

andares2e3

Alguns Servi√ßos de Integra√ß√£o se relacionam a mecanismos dos pr√≥prio servidor de aplica√ß√£o que n√£o est√£o diretamente relacionados √† aplica√ß√£o como o Servi√ßo de Transa√ß√£o, Servi√ßo de Logging e Servi√ßo de Autentica√ß√£o. Estes tamb√©m s√£o servi√ßos reutiliz√°veis entre aplica√ß√Ķes, mas n√£o s√£o providos por outros sistemas, est√£o embutidos com contexto de execu√ß√£o do servidor de aplica√ß√£o , ou, como diriam alguns, eles s√£o o Servidor de Aplica√ß√£o. Por outro lado, quase nunca usamos diretamente os contratos do servi√ßos de infraestrutura. Por exemplo, ao usar o servidor de e-mail, usamos a API Java Mail para a comunica√ß√£o, mas depois abstra√≠mos essa API numa API pr√≥pria, mais simples, mais pr√≥xima da necessidade do sistema. Isso porque n√£o queremos ficar presos os objetos da API Java Mail para criar nossos e-mails. Eles podem ser criados em qualquer camada de cima, ou at√© serem¬† entidades do dom√≠nio.¬† O mesmo acontece quando usamos a API JDBC para SGBD ou trabalhamos com arquivos usando a API de I/O. Apenas o andar de integra√ß√£o deve saber como essas API “de baixo n√≠vel” funcionam e como elas mapeiam para o contratos dos Servi√ßos de Integra√ß√£o.¬† Desta forma quando precisarmos utilizar nosso dom√≠nio em uma estrutura de servidor “fisicamente diferente”, mas que cont√©m os mesmos servi√ßos, podemos fazer isso atrav√©s de utilizar uma camada de integra√ß√£o diferente, mas que exp√≥e os mesmos contratos de servi√ßo que antes. Isto √© realmente muito importante para a vida do produto. A tecnologia evolui muito rapidamente. Hoje temos tecnologia de Cloud e servidores el√°sticos que ha 10 anos n√£o t√≠nhamos.¬† Portar um sistema de uma arquitetura de ha 10 anos para cloud √© imposs√≠vel sem a camada de integra√ß√£o. √Č necess√°rio reescrever o sistema ou pelo menos parte dele para que ele seja compat√≠vel. O aparecimento do Google App Engine (GAE) deixou isto bem claro quando escolheu prover os mesmos servi√ßos de persist√™ncia e e-mail de uma forma completamente diferente. Inclusive sem o uso de um banco relacional. Hoje em dia, mesmo com os avan√ßos do GAE¬† ainda √© preciso ter certos cuidado, cuidados esses que deveriam ser responsabilidade de um andar de Integra√ß√£o de forma a que nenhuma camada acima tenha que sofrer o impacto da migra√ß√£o de servidor.

Uma boa arquitetura é aquela que poupa seu dinheiro. Não necessariamente no momento da construção, mas principalmente no momento da modificação, da evolução e da adaptação a novas ideias, metodologias, cenários, tecnologias ou ambientes. Seu produto é seu domínio, seu core. E esse é seu ativo (asset) principal. Reescrever outro, significa gastar recursos que já gastou antes. Retrabalho. Cada andar é desenhado com a mesma lógica de proteção. Proteção da Experiencia do Usuário (UX) no andar de Visualização. Proteção de regras de navegação e apresentação no andar de Apresentação. Proteção do core do negócio no andar do Domínio. Proteção de todos os anteriores de mudanças de relacionamento com outros sistemas e ambiente de execução no andar de Integração.

Voc√™ pode recordar as aplica√ß√Ķes em que trabalhou e comparar com este modelo. Quantas das aplica√ß√Ķes que lembra tinham andar de Apresenta√ß√£o separado do de Visualiza√ß√£o ? Quantas usavam Servi√ßos P√ļblicos para n√£o expor os servi√ßos de Dom√≠nio ? Quantas isolavam intera√ß√Ķes com outros sistemas usando servi√ßos especiais de integra√ß√£o ? Quantas realmente poderiam modificar seu cliente visual , n√£o apenas em look and feel mas tamb√©m em tecnologia ( n√£o apenas mudar o layout web ,mas mudar de web para desktop) ?¬† Quantas implementavam um cliente HTTP/HTML pobre e quantas foram evolu√≠das para um cliente mais rico usando Ajax ? Quantas fizeram isso preservando o cliente original ?
Quanto dinheiro, tempo e esfor√ßo foi desperdi√ßado ao longo da vida dessas aplica√ß√Ķes ?

Vivemos numa era de crise de valores. Hoje √© mais aceit√°vel fazer r√°pido do que fazer bem. Todos olham quanto custa criar o sistema, mas pouco olham quanto custa manter o sistema. E se algu√©m faz um sistema em 2 ou 3 anos, √© para ele ser usados por 20 ou 30. Logo, o custo est√° no longo prazo e n√£o no curto.¬† Mas infelizmente vivemos na era do “projeto” e n√£o do “produto”. Pelo menos no Brasil. Os clientes querem um produto, mas o que vendemos para eles √© um Projeto. E projetos ha muitos. Produto ha s√≥ um. Alguns clientes aprendem na pr√°tica e duras custas quando s√£o enganados por aquela F√°brica de Software que fez um contrato para ‘um Projeto de Software” e falhou. E o cliente contrata uma, duas, tr√™s F√°bricas de Software e sai sempre de m√£os vazias. Alguns clientes se contentam at√© com apenas um contrato para “a especifica√ß√£o de um Projeto de Software”. Isso √© como voc√™ comprar a planta da casa sem nenhum compromisso que algum dia aquela casa ser√° constru√≠da. O problema disso √© que essa planta tem validade. Uma validade imposta pelo mercado, a tecnologia, e as pr√≥prias ideias do cliente que evoluem. O que era ontem, n√£o ser√° amanh√£. Isto √© a √ļnica coisa que podemos ter a certeza. E a nossa arquitetura deve lidar com esta realidade.

E n√£o se confunda o que estou dizendo com alguma tentativa de dizer que a onda √°gil est√° errada e que devemos voltar √†s especifica√ß√Ķes de uml. Nada disso. Bem pelo contr√°rio. Mas tamb√©m n√£o se confunda acreditando que √© poss√≠vel construir um sistema que dure no tempo e proteja seu dinheiro , sem ver e entender “the big picture” e os erros cometidos ao longo da historia.¬† Design voc√™ pode evoluir como quem muda a cor das paredes ou mudas os cortinados e os abajures. Mas arquitetura n√£o evolui. O banheiro sempre ser√° onde √©. A cozinha sempre ser√° onde existe calor e √°gua para cozinhar. Mudar essas coisas n√£o √© evoluir a arquitetura. √Č fazer outra arquitetura. Mas destruir para construir de volta √© jogar dinheiro fora. Em software parece que n√£o √© t√£o caro, porque estamos s√≥ mexendo com bits , tem virtualmente custo zero. Mas o que custa n√£o √© partir pedra ou mudar bits. O que custa √© partir pedra da forma errada ou mudar os bits da forma errada. E qual √© a forma errada ? Aquela que n√£o perdura. Stonehenge, as Pir√Ęmides, a Muralha da China, e tantas outras constru√ß√Ķes s√£o consideradas boa arquitetura porque perduraram. Ningu√©m lembra dos edif√≠cios obliterados pelo tempo.

Arquitetura de software n√£o tem assim tantas configura√ß√Ķes possiveis. Todas s√£o repeti√ß√Ķes ou aglomerados de 3 tipos b√°sicos de padr√£o : cliente-servidor, peer-to-peer (P2P) e publish-subscribe (Mensageria). S√≥ isso. De longe a mais usada √© Cliente-Servidor, mas hoje em dia o mais comum √© termos um hibrido entre as tr√™s. Pegue uma aplica√ß√£o JEE comum. Temos Cliente-Servidor, claro, mas temos mensageira (JMS, Java Mail, ESB) e P2P (cluster, cloud, NoSQL). N√£o ha como driblar este fato. Os ativos da sua empresa ou os do seu cliente v√£o estar embutidos na arquitetura que escolher e nas decis√Ķes que tomar. Quanto menos decis√Ķes tomar, melhor. Ou seja, quanto menos coisas tiver que restringir a pontos fixos melhor. E isso significa abstrair o que pode mudar em m√≥dulos. E esses m√≥dulos em camadas. E as camadas em andares. E os andares em nodos.

Criar uma aplicação com a arquitetura padrão que descrevi tem o mesmo custo que criá-la sem. Uma camada a mais é mais uma forma de organização do código do que uma questão de ter mais código ou menos. Logo, o esforço não é braçal, mas intelectual. E o trabalho intelectual sempre tem a mesma velocidade. Logo, não é o custo que impede as empresas ou os desenvolvedores de criarem arquiteturas robustas e sim a sua falta de conhecimento ou a sua falta de comprometimento com o retorno financeiro da aplicação que estão construindo.

Espero que depois que leu isto, veja suas aplica√ß√Ķes de outra forma e entenda porque aquele sistema Z teve que ser redesenhado 2 anos depois de ter sido entregue, ou simplesmente foi jogado fora e um outro foi feito.

24 comentários para “Arquitetura Padr√£o Completa”

  1. Fala Sérgio,

    Eis um pensamento “fora da caixa”. Sua abordagem me fez pensar e pensar sobre o que significa o termo “Arquitetura” e como ela est√° sendo vendida atualmente. Tudo o que disse √© totalmente verdade nos dias de hoje e assim persistir√° por mais um longo tempo.

    Acredito que at√© uma arquitetura orientada a servi√ßos, que foge desta receita de bolo por decompor tudo em servi√ßo, diminuindo o DRY; ainda est√° desenhada com este mesmo cerne descrito por voc√™ e assim, fatalmente est√° a merc√™ de retrabalhos e altos acoplamentos dentro de cada m√≥dulo do “produto”, concorda?

  2. SOA √© apenas uma forma de organizar o andar de dom√≠nio. Deveria ser uma forma de organizar os servi√ßos p√ļblicos e proteger os servi√ßos de dom√≠nio se aplica√ß√Ķes diferentes e de certa forma ajudar na integra√ß√£o tamb√©m, mas realmente – concordo – os servi√ßos acabam mais vezes que menos sendo altamente acoplados a quem os chama. E muitas vezes o pr√≥prio servi√ßo de dominio √© exposto em vez de uma fa√ßade. Isto era assim no EJB, √© assim no ESB. As ideias ainda s√£o as mesmas. √Č dificil para as pessoas entenderem o que √© core, e como isol√°-lo. E isso adv√©m de que elas n√£o foram ensinadas a isol√°-lo, nem lhes foi mostrado porque isso √© vantajoso. O EJB 2 era feito com esse objetivo, e as pessoas achavam muito ruim. Porqu√™ ? Porque o usavam da forma errada. Tudo era remoto, em vez de algumas coisas serem locais (core) e algumas coisas serem remotas (publicas, fa√ßade). Hoje em dia nem tem mais essa distin√ß√£o. Tudo √© local, e √© ainda mais dificil destinguir o que √© servi√ßo de dominio do que √© servi√ßo de fa√ßade. E ai vc joga o ESB no meio e ai sim tudo liga com tudo e vira um anti-layer. Uma pena realmente.

  3. Muito interessante essa arquitetura apresentada. Estou com uma d√ļvida. Pensando no seguinte requisito: antes de cadastrar um usu√°rio no sistema devo verificar seu CPF, em algum servi√ßo de terceiros para essa finalidade. Esse servi√ßo pode ser chamado dentro de um Domain Service? Ou deveria ficar num Application Service?

  4. Nesse caso em especial ficaria dentro de um Validador que depois seria usado pelos serviços de domínio onde necessário.Isto porque se trata de uma regra de validação. O fato de ser um terceiro a validar é um detalhe. Do ponto de vista da app é o validador que está fazendo esse trabalho.
    O serviço de validação invocado, neste caso, seria parte do andar de integração. Ele mesmo seria um serviço de integração.
    Outros servi√ßos de integra√ß√£o – que n√£o sejam de valida√ß√£o – podem ser chamados diretamente por outros servi√ßos, sejam eles de integra√ß√£o , dom√≠nio, ou fa√ßades. N√£o sei o que quer dizer com “Application Service”.

  5. Application Service, seria a camada de serviços que orquestram os Domain Services, para quando haja alguma funcionalidade que precise chamar vários domain services em uma ordem específica.

  6. Minhas principais d√ļvidas surgem por normalmente trabalhei meus domain services, chamando repositorios, que representam onde armazeno meus dados, normalmente um banco de dados.

    Mas nunca trabalhei com funcionalidades mais avançadas, onde por exemplo dentro de um domain services alem de chamar meus repositorios, chamasse também outros serviços da infra, como webservice de terceiros. Nem sei se é correto fazer essa mistura.

  7. Para tentar explicar melhor, por exemplo num domain service UserService, tenho um método registerUser(User user).

    Dentro desse m√©todo, fa√ßo algumas valida√ß√Ķes do user, depois chamo o reposit√≥rio para adicionar no banco e por √ļltimo envio um email para esse novo user.

    Esse passo de enviar email, utilizando um emailService, dentro do método registerUser está correto?

    Ou somente deveria trabalhar no domain services com coisas relacionadas aos repositorios de dados.

  8. Está correto. Dentro da camada de domínio um serviço e um repositório valem o mesmo. Ou seja, um serviço pode chamar outro serviço ou pode chamar um repositório.
    Um serviço pode chamar outro serviço do domínio ou um serviço de integração. O EmailService seria um serviço de integração.
    O primeiro serviço chamado ( que vc chama de serviço de aplicação) é responsável pela fronteira da transação. Então, no seu exemplo, enviar o email e registrar o usuário no banco é uma transação só. Não queremos registrar sem enviar o email e vice-versa. Dentro do dominio não ha essa precoupação pois o foco é com o negocio e não com a infraestrutrua tecnologico. Mas todos são serviços.
    O domain service não se restringe a conversar com os repositórios, isso seriam os serviços crud muito básicos. Mas os serviços de domain de processo que são aqueles que realmente fazem alguma coisa (embora possa parecer apenas um crud, mas sempre tem mais alguma coisa tipo o cadastro de pedido que dispara o envio da mercadoria) são mais complexo. O que não deve ser feito é criar um serviço de domínio que simplesmente é uma casca do repositório. Isso não tem sentido.
    Os √ļnicos servi√ßos que podem ser uma casca s√£o os de aplica√ß√£o.

  9. Acho que entendi. Então em meu domain service, UserService, dentro do método registerUser posso chamar tranquilamente meu repositorio de user e meu serviço de email, sem medo de estar fazendo gambiarra.

    Essas d√ļvidas surgem muito tamb√©m porque normalmente eu fazia o paralelo de domain service com minhas antigas stored procedures que tinham as regras de negocio, porem somente referente a dados, nada de envio de email, etc.

  10. Gostei, muito do artigo.
    Já li umas 4 vezes como referência.

    Mas uma d√ļvida, qual camada ficaria respons√°vel pela formata√ß√£o dos valores inseridos pelo usu√°rio para armazenamento?
    Exemplo simples, nome e outros dados em letra maiuscula, tamanho maximo de strings, formata√ß√£o de ceps, cnpjs e outros documentos e por ae vai….
    Eu geralmente faço esse tratamento na view, com java script e após na action que recebe esses dados.

  11. Como o nome indica “formata√ß√£o” √© uma coisa puramente visual. Portanto, na camada de visualiza√ß√£o.
    Os formatadores s√£o objetos que convertem de um objeto de classe X para String e vice-versa. Os classicos formatadores de data, cnpj, etc…
    O Truque √© nunca transitar o dado formatado. A Visualiza√ß√£o sempre ir√° converter para o objeto do tipo X e √© esse dado que trafega no resto do sistema. a String formatada √© “for user eyes only”

    Coisas como letra mai√ļscula , para mim pessoalmente, eu condeno. Nao ha raz√£o hoje em dia para isso. N√£o estamos no clipper e java suporta UTF-16. Se for mesmo necess√°rio, n√£o √© uma responsabilidade da visualiza√ß√£o porque vc est√° modificando os dados (est√° destruindo informa√ß√£o na realidade). Ent√£o tem que ser em algum ponto do dominio ou da camada de fa√ßade. Este ponto √© bem ambiguo. Eu precisei fazer isto uma vez e acabei precisando que v√°rias camadas fossem conscientes do assunto. N√£o √© legal. Mas n√£o √© legal porque o requisitos em si √© furado.

    Javascript √© legal para controlar em realtime e formatar em realtime , mas √© sempre bom ter um filtro ou um interceptor ou qq coisa que fa√ßa o meio campo. Uma coisa que d√° muito problema √© datas. Se o usu√°rio colocar uma data que n√£o existe ( 30/2/2013) o que o formatador faz ? se formatar para null, o validador ir√° acusar que o usuario n√£o colocou o campo, o que √© mentira. Se ele tentar converter para Date vai dar erro porque n√£o existe. Ent√£o, √†s vezes √© melhor trabalhar com objetos de view, e ter uma valida√ß√£o desses objetos na visualiza√ß√£o exatamente por causa das formata√ß√Ķes. Se entrar i18n , ent√£o, fica ainda mais complexo. E tudo isso √© coisa da visualiza√ß√£o. Depois que est√° “visivelmente v√°lido” podemos converter para os objetos reais como Date. Aqui j√° sabemos que n√£o ter problema de formato. Resta saber se ter problema de regra ( tipo a data √© fora de um range de negocio qualquer… ). Parece que estamos validando a mesma coisa 2 vezes mas n√£o. Na primeira estamos validando o input “cru” propriamente dito, e na segunda a regra de negocio. Ent√£o √© bom que al√©m do javascript sempre haja valida√ß√£o do lado do server, tanto no andar de visualiza√ß√£o como na de servi√ßos. √Č que no futuro esses servi√ßos podem ser uados por outro andar de visualiza√ß√£o diferente e quem garante que elas verificam essas coisas ? O servi√ßo tem que se proteger. Afinal ele tem que fazer cumprir o seu contrato.

  12. Ol√° Sergio,

    Você tem algum projeto com o código-fonte para vermos que use essa arqutitetura (ou o mais próximo dela)?

  13. Infelizmente, neste caso, porque eu trabalho em aplica√ß√Ķes para outros, eu n√£o detenho o c√≥digo de nenhuma das aplica√ß√Ķes que j√° fiz, ent√£o infelizmente n√£o ha nenhuma que pudesse apresentam o c√≥digo fonte. Por outro lado, n√£o sei se olhar o c√≥digo fonte ajuda se os conceitos n√£o estiverem bem assentes.
    Tem alguma d√ļvida especifica ?

  14. Obrigado por responder. Ok, n√£o tem problema.
    Eu fiquei em d√ļvida na se√ß√£o de apresenta√ß√£o (presenters).

  15. Vou especificar: tenho d√ļvida de como ficaria o c√≥digo dessa se√ß√£o (realmente √© uma √≥tima ideia!), como seriam os objetos e as fun√ß√Ķes.

  16. Ol√° Sergio grande artigo,na verdade grande blog.
    tenho uma arquitetura bem parecida com o proposto aqui em outro post do blog http://www.javabuilding.com/architecture/arquitetura-com-domainstore-repositorio-e-query-object.html

    no entanto tenho algumas d√ļvidas em rela√ß√£o √† minha arquitetura,por exemplo tenho meu repositorio:
    @Stateless
    public class ContratoRepository extends AbstractRepository{
    @PersistenceContetx
    private EntityManagem em;
    public void save(Contrato c){em.persist(c);}
    public Contrato find(Integer id){return em.find(Contrato.class,id)}
    }
    tenho minha classe Service, que chama meu reposit√≥rio e outros servi√ßos e outros reposit√≥rios,como esse servi√ßo vai ser acessado por 2 sistemas diferentes onde cada sistema s√≥ poder√° fazer algumas opera√ß√Ķes esse servi√ßo implementa 2 interfaces,vamos supor
    @Local
    public interface ContratoService{
    public Contrato find(Integer id);
    }

    @Local
    public interface ContratoAdmService{
    public Contrato find(Integer id);
    public void save(Contrato c);
    }

    @Stateles
    public class ContratoService implements ContratoService,ContratoAdmService{

    @EJB
    private ContratoRepository repository;
    @Inject
    private EmailService emailService;
    public void save(Contrato c){
    Validator.validate(c);
    repository.save(c);
    emailService.send();
    }
    }

    no meu controller do ZK (MVVM) eu chamo meu reposit√≥rio para opera√ß√Ķes de escrita e meus services para opera√ß√Ķes onde h√° mudan√ßas de estado

    public class ContratoVM{

    @EJB
    private ContratoAdmService contratoService;
    @EJB
    private ContratoRepository repository;
    private Contrato contrato;

    @Command
    @NotifyChange(“contrato”)
    public void save(){
    contratoService.save(contrato);
    }

    @Command
    @NotifyChange(“contrato”)
    public void find(@BindingParam(“id”) Integer id){
    this.contrato = repository.find(id);
    }
    }

    Você considera correto eu ter um entitymanager agindo como o domainstore dentro do meu repositório ou deveria isolar isso em outra camada?

    posso ter problemas injetando esse monte de @EJB?

    no geral esta arquitetura est√° boa?

  17. Sim, a sua arquitetura est√° boa. Acho que vc quiz dizer que chamava o repository em opera√ß√Ķes de leitura (em vez de escrita).
    Sendo que vc est√° usando um servidor EE n√£o ha problema em usar as anota√ß√Ķes dele nem o EntityManager dele. Sendo que est√° escondido no repositorio n√£o vejo problemas.
    Vc não mostrou como faz queries complexas (filtros , por exemplo) Dependendo da complexidade só ai tlv valha a pena incluir um query object, mas se já usa o JPA 2 provavelmente é um trabalho desnecessário.
    Como tudo est√° dividido se vc precisar disso no futuro pode facilmente adicionar. Por agora, acho que t√° show.

    Abraços

  18. Ol√° Sergio obrigado por responder,sim houve um engano meu quiz realmente dizer que chamava o repository em opera√ß√Ķes de leitura.Tenho algumas consultas bem complexas todas em JPQL estava pensando em migrar para a Criteria do JPA2, ainda n√£o o fiz porque n√£o domino esta API ainda.

    Uma outra d√ļvida que tenho quanto a arquitetura , √© sobre o tal do modelo an√™mico, vc inclusive j√° postou sobre isso e compartilho da sua opini√£o que o pessoal t√° vendo fantoches em tudo, mas mesmo assim gostaria de uma opini√£o.O referido sistema que apresentei o trecho acima √© um sistema de auditoria de contratos,onde a entidade Contrato possui um Status e esse status √© que gerencia o fluxo do processo
    @Entity
    public class Contrato{
    @ManyToOne
    @JoinColumn(name=”st_id”)
    private Status status;
    }
    atualmente na minha arquitetura o código que muda o status do contrato está em minha classe ConTratoServiceImpl

    public void enviarContratoRevisao(Contrato c){
    Status status = statusRepository.findStatus(STATUSLIST.EM_REVISAO);
    c.setStatus(status)
    contratoRepository.update(c);
    }
    é correto eu ter este tipo de operação em minhas classes de serviços ou este método deveria estar dentro da entidade Contrato? é correto chamar meu repositório dentro de minhas entidades?

    @Entity
    public class Contrato{
    private StatusRepository statusRepository;
    private ContratoRepository contratoRepository;
    @ManyToOne
    @JoinColumn(name=”st_id”)
    private Status status;

    public void enviarParaRevisao(){
    Status status = statusRepository.findStatus(STATUSLIST.EM_REVISAO);
    this.status(status)
    contratoRepository.update(this);
    }
    }
    Abraço.

  19. Eu acho isso meio estranho. Para mim o status não passa de num enum e pronto. Para quê tanta coisa em recuperar um status do banco só para colocar num campo ?
    Um enum resolve suficientemente bem essa situação. O status é uma propriedade do objeto nada mais que isso.
    public Contrato enviaParaRevisao(){
    Contrato novo = new Contrato(this); // copia todos os campos
    novo.status = StatusContato.EM_REVISAO;
    return novo;
    }

    depois vc manda para o serviço normalmente

    Contrato contrato = …
    servico.save(contrato.enviaParaRevisao())

    N√£o vejo vantagem em colocar o repositorio no entidade. isso d√° uma dor de cabe√ßa…. antigamente no EJB 2 era para fazer assim e ninguem fazia, agora que o EJB evolui com o JPA vamos andar para tr√°s ? Acho que n√£o.

    Se vc quiser pode até criar um outro objeto ContratoState (que controla o estado, não status) conforme alguma regra de negocio e tem acesso a serviços e repositorios, mas o jeito que está fazendo me parece muito complexo sem necessidade.

  20. Boa tarde, agradeço as respostas,realmente faz mais sentido que Status seja apenas um Enum, mesmo que seja uma Enum mapeada com JPA.
    Quanto aos repositórios nas entidades, foi só um exemplo,apenas queria saber sua opinião,não uso,acho feio,e dá responsabilidades que não são da entidade não gosto de coisas como activeRecord.
    J√° me desculpando se eu estiver chato, tenho mais uma d√ļvida que √© sobre a cria√ß√£o de objetos, acredito que o mais correto seria utilizar factories e estas estariam na camada de dominio,estou correto? e de onde eu invocaria minha factory? do reposit√≥rio,da service ou no pr√≥prio Controller? outra se no meu construtor eu precisar de outra entidade,que pra criar um objeto eu precise consultar outros antes.Vamos supor que todo contrato criado j√° deve estar vinculado a um fiscal(exemplo ruim j√° teria o fiscal na mem√≥ria apenas passaria como par√Ęmetro mas √© apenas para entendimento)

    public class Contrato(){
    private Fiscal fiscal;
    public Contrato(Fiscal fiscal){
    this.fiscal = fiscal;
    }
    }

    é correto eu injetar o repositório dentro da factory para fazer a consulta? ou se eu fizesse:
    public class ContratoRepository extends …{

    @Inject
    ContratoFactory factory;
    public Contrato novoContrato(Integer fiscId){
    Fiscal fiscal = fiscalRepository.find(fiscId);
    return factory.createNewContrato(fiscal);
    }
    }
    estaria correto?ou ainda se eu quiser eliminar a factory,por quest√Ķes de simplicidade, posso delegar ao reposit√≥rio a cria√ß√£o de objetos?

  21. N√£o est√° de nenhuma forma ser chato. Quisera eu que todo o mundo tivesse duvidas como as suas.
    Sendo um repositorio compar√°vel a uma lista na memoria , seria preciso criar o objeto antes de envi√°-lo √† lista. Portanto, n√£o √© o reposit√≥rio que sabe criar os objetos no sentido que n√£o existe RepositorioX.getNovoX(). Isso nos deixa com os m√©todos tradicionais baseados em construtor, m√©todos est√°ticos de constru√ß√£o e factories. Mas factories implicam em que existe uma forma mais complexa que as anteriores e isso n√£o √© comum. Quando A depende de B como no seu exemplo vc n√£o cria um construtor A(B) mas sim um campo B em A e valida em tempo de “save” se a rela√ß√£o √© correta, obrigat√≥ria, etc… mas conceitualmente n√£o ha esse construtor. Contudo pode acontece que seus objetos realmente n√£o tenha construtores publicos. Um cl√°ssico s√£o os objetos de valor como Money que normalmente t√™m algo como Money.valueof(). Bom, no fim, se pensarmos que existe uma objeto que sabe criar o outro seja porque ele invoca o construtor, o m√©todo est√°tico ou qq outro mecanismo isso seria uma f√°brica. Nesse caso √© o reposit√≥rio que depende da f√°brica e n√£o o inverso. O reposit√≥rio usa a f√°brica quando precisar criar os objetos (por exemplo quando os l√™ do banco) mas a fabrica pode ser usada em outros lugares. Por exemplo no controler quando se transforma algum request nos dados do objeto. A factory existe para substituir o new, ent√£o ser√° usada onde normalmente seria usado new.

    Normalmente o reposit√≥rio n√£o trata da cria√ß√£o dos objetos porque ele delega isso a um DomainStore ( O EntityManager √© um DomainStore). O domain store se baseia fortemente em metadados e em conven√ß√Ķes. Normalmente ele assume que os objetos t√™m um construtor sem parametros, mas algumas implementa√ß√Ķes deixa definir como os objetos s√£o criados ( o UserType do Hibernate serve para isso). O √ļnico objetivo do reposit√≥rio na leitura √© definir a query, n√£o execut√°-la. √Č quem executa que ter√° acesso √† factory. A menos √© claro que n√£o utilize uma camada de domain store, mas hoje me dia, duvido. Em ultimo caso ser√° o reposit√≥rio a saber dar o “new” seja ele explicito ou via factory. Mas normalmente isso existe embutido na camada de baixo. Ali√°s porque se A depende de B quando eu fizer uma query de A que tem B, o mecanismo precisa saber criar A e B e n√£o apenas A. Portanto o reposit√≥rio de A teria que saber criar B tb e todas as entidades acopladas a A e B e a essas outras. √Č por isso que o padr√£o DomainStore que se baseia em metadados √© mais √ļtil aqui. existe um √ļnico lugar central que sabe tratar , criar, persistir e reler os ojectos do dom√≠nio. Espero ter respondido suas perguntas a contento.

  22. Ol√° Sergio, esclareceu sim, obrigado!

  23. Ol√° Sergio, tudo bem?

    Tenho lido seus artigos sobre MVC e Arquitetura Java e percebi que você entra em detalhes que são pouco abordados nos artigos que se vê por ai. Mas eu senti falta das referências nos seus artigos, eu queria me aprofundar em entender melhor essa sua visão de arquitetura queria que me indicasse os livros que você leu para obter esse conhecimento.

    Obrigado!

  24. Obrigado por ter notado que me preocupo com detalhes que não estão nos livros. Este é o objetivo do blog. A razão é porque os livros costumam ser muito descritivos e por isso limitados na discussão dos porquês e por que nãos.
    O que eu comento no blog n√£o tem uma bibliografia associada , afinal esse √© o ponto da quest√£o, mas existem sim alguns livros que li. Se eles me ajudaram a chegar nas conclus√Ķes ou n√£o cabe a voc√™ avaliar, mas eu diria que a li√ß√£o maior que tirei dos livros √© que as pessoas no mercado ignoram muita da teoria que existe e partem para uma pr√°tica meia boca sem um estudo pr√©vio e muitas vezes esta pr√°tica √© contr√°ria √† teoria, o que √© pior.

    Alguns livros seriam
    – Padr√Ķes de Projeto em Java – Steven John Metsker (Bookman)
    РCore J2EE Patterns 2 Edição (Prentice Hall)
    – Modelagem √Āgil – Scott E. Ambler ( Bookman)
    – Software Arquitecture in Practice (Addison Wesley)
    РSoftware Requirements 2 edição РKarl E. Wiegers (Microsoft Press)
    – Software Requirement Patterns – Stephen Withall (Microsoft Press)

Comente

Enquete

Sobre quais assuntos gosta de ler neste blog ?

View Results

Loading ... Loading ...

Artigos