autor: Marc Alcântara

Introdução

Este artigo tem o objetivo de mostrar que é possível construir softwares de missão critica na WEB modelando inteiramente orientado a objetos, ganhando com isso as vantagens que uma arquitetura flexível nos dá, como escalonamento. Nunca antes isso fez mais sentido sobre tudo com a possibilidade de qualquer pessoa física poder hoje contratar uma infraestrutura inteiramente configurável desde CPU e gigas de memória a até mesmo outras máquinas virtuais e clusters de banco de dados construindo um verdadeiro data center sem ter que necessariamente arcar com os custos de tê-lo fisicamente em um espaço próprio.

Com a evolução dos métodos de segurança na rede para as aplicações, as empresas como Google, Red Hat , Microsoft e Amazon massificaram a venda de serviços de infra estrutura na nuvem. Com isso, voltou aquela discussão de investir ou não em praticas de engenharia de softwares em seus negócios, mais especificamente em sistemas que servem como plataforma para empresas rodarem suas operações.

Não é segredo que a maioria das empresas em solo brasileiro usam sistemas onde sua arquitetura se define em controlar a transação com o banco de dados, assegurar a integridade funcional dos dados com validações e algoritmos bem validados junto a área de negócio e que funcione rápido o suficiente para que seja funcional. Tendo isso garantido, não importa se a logica de negocio está uma parte na camada de cliente, ou se esta inteiramente dentro de procedures de banco de dados, algo que em realidade ajuda na performance e na simplicidade de execução.

E isso não está errado! Muito pelo contrario! Até porque o objetivo de uma corporação é operar em uma plataforma segura e que não comprometa negativamente as ações dos seus negócios, além do fato de que a maioria dos produtos tecnológicos no mercado como plataformas e linguagens darem mais ênfase na cobertura de operações corporativas de forma pragmática. Em uma palestra de Ralph Johnson escutei ele pessoalmente dizer que o importante é você ter em conta as vantagens e desvantagens de cada estratégia que irá adotar, não é somente o fato do uso correto de um padrão que garantirá o bom funcionamento de um sistema. Muitas vezes até o exagero do seu uso nos projetos e na definição de requisitos, pode resultar em ser extremamente negativo. Pode por exemplo, complicar o que deveria ser simples, inclusive na na comunicação da equipe ou ainda exigir um conhecimento abstrato da equipe que não esteja especificamente preparada ou casos que o desenho leva a um aumento de latência das operações por excessos nas divisões de camadas. O próprio Ralph Johnson citou na mesma palestra: – Você pode se envenenar até mesmo de água, caso consumida em excesso.

 

Padrões de Arquitetura

Usando as classificações dadas por Martin Fowler podemos separar em 3 padrões de arquiteturas:

Transactional Script

Este é um padrão muito usado encontrado em sistemas de gestão que não dependem diretamente de integração com outros sistemas em ambientes ou tecnologias diferentes. Basicamente recebe um conjunto de dados de input , faz todas as validações em um mesmo bloco de instruções ou subdivide estes blocos em um conjunto de sub funções. Armazena os dados de forma direta e acoplada.

Contém muitas vantagens e não é por qualquer razão que grande parte dos sistemas que estão rodando hoje nas instituições estão neste padrão. Pois é procedural e simples para que a grande maioria dos desenvolvedores possam compreender. Funciona muito bem conectando a um único objeto de acesso a dados, onde Martin Fowler chama Row/Table Data Gateway. E é simples de controlar os limites de transações.

Table Module

Este é o padrão mais usado nos sistemas de gestão que  observei até hoje. Ele muitas vezes é descrito como um meio termo entre o Transactional Script e o Domain Model. Porque organiza o desenho do domínio em tabelas ao invés de procedimentos, fornecendo uma melhor estrutura. Porém impede o uso de inúmeras técnicas que o Domain Model fornece, como padrões de orientação a objetos heranças, estratégias e etc.

Devido a essas vantagens e simplicidade os principais produtos de desenvolvimento foram feitos para atender a demanda de negócios cada vez mais crescente, por exemplo o Microsoft COM e .NET. Com uma abordagem mais simples seriam mais dissemináveis nos projetos pelo globo. Não é de se estranhar o grande numero de aplicações de missão critica rodando com a lógica de negocio em stored procedures e na parte de apresentação contar com uma chamada a um único objeto de acesso a dados muitas vezes denominados como DAO.

Domain Model

Este é o oposto do transaction script, porque ao invés de ter toda a lógica em uma rotina, o Domain Model organiza a lógica de negocio dentro de um modelo representacional que é desenhado ao inicio, onde suas tabelas são extraídas dos substantivos do domínio de aplicação e os métodos ou casos de uso são retirados dos verbos. Um sistema de vendas no varejo por exemplo teria classes como: Produto, Carrinho, Compra, Fatura, Cliente. E um caso de uso seria Finalizar Compra, ou Registrar Cliente.

Esse padrão de arquitetura é o que vamos tratar neste artigo, por entender que se beneficia de todas as vantagens da orientação a objetos e das boas praticas da engenharia de software. Mesmo sabendo que a sua aplicação é mais complicada e um pouco frustrante para quem não ainda está habituado com este paradigma recomendo sua utilização para o problema em questão que é arquitetura de aplicações em cloud.

captura-de-tela-2017-05-08-as-20-24-58

 

 

 

Cloud Computing

O conceito do cloud computing se refere ao uso de memória ou capacidade de armazenamento e computo compartilhados e interligados a outros servidores pela internet utilizando o principio GRID (computação em grade). Com sua evolução foram criados 7 tipos:

IaaS – Infrastructure as a Service ou Infraestrutura como Serviço (em português): quando se utiliza uma percentagem de um servidor, geralmente com configuração que se adeque à sua necessidade. (p. Ex.: Softlayer)

PaaS – Plataform as a Service ou Plataforma como Serviço (em português): utilizando-se apenas uma plataforma como um banco de dados, um web-service, etc. (p.ex.: IBM Bluemix, Windows Azure e Jelastic).

DaaS – Development as a Service ou Desenvolvimento como Serviço (em português): as ferramentas de desenvolvimento tomam forma na computação em nuvem como ferramentas compartilhadas, ferramentas de desenvolvimento web-based e serviços baseados em mashup.

SaaS – Software as a Service ou Software como Serviço (em português): uso de um software em regime de utilização web (p.ex.: Google Docs , Microsoft SharePoint Online).

CaaS – Communication as a Service ou Comunicação como Serviço (em português): uso de uma solução de Comunicação Unificada hospedada em Data Center do provedor ou fabricante (p.ex.: Microsoft Lync).

EaaS – Everything as a Service ou Tudo como Serviço (em português): quando se utiliza tudo, infraestrurura, plataformas, software, suporte, enfim, o que envolve T.I.C. (Tecnologia da Informação e Comunicação) como um Serviço.

DBaas – Data Base as a Service ou Banco de dados como Serviço (em português): quando utiliza a parte de servidores de banco de dados como serviço.

Em uma arquitetura Transactional Script onde se mantém toda a sua lógica e seus dados em um mesmo bloco, e caso imaginarmos um cenário que pede um escalonamento vertical como por exemplo trabalhar com vários repositórios de dados em simultâneos, já teriamos um grande problema. Se precisar trabalhar com sistemas integrados de forma online, essa arquitetura tampouco seria funcional.

Em uma Arquitetura Table Module, caso necessario trabalhar com múltiplos servidores web em loadballacing ele atenderia, porem se voltamos a pensar se funcionaria com múltiplos servidores de banco de dados dados diferentes de forma online, também teriamos um grande problema. Ou caso se encontre uma solução a isso, o custo de manutenção tenderia ser mais elevado que o de um domain model.

Em resumo, o aparecimento do clound computing exige uma arquitetura escalável e requer realmente seu uso. Sobretudo se pensamos na maturidade do produto a longo prazo. E refletindo a respeito da engenharia de software podemos concluir que a solução para esse novo requerimento arquitetônico seria uma arquitetura com baixo nível de acoplabilidade utilizando para isso técnicas e padrões de programação e modelos orientados a objetos utilizando o Domain Model.

Podemos pensar no exemplo de criar um sistema na nuvem como um SaaS, de forma que sua infraestrutura estaria totalmente na nuvem em um serviço IaaS da Amazon. Tendo uma arquitetura física de 1 servidor web outros 2 de banco de dados e um outro de arquivos este utilizado para upload de dados oriundos de um outro sistema. Ainda devendo compartilhar uma serie de serviços na WEB para que aplicações mobile possam interagir com o sistema. Uma arquitetura Domain Model seria a ideal para este problema em questão.

Outra razão da escolha deste padrão de arquitetura, é respeitar duas leis básicas da engenharia de software, o limite das possibilidades de alteração de código tende ao infinito, pois se trata de um produto abstrato gerado a partir do intelecto humano sem a necessidade de materiais. Outra lei é: Quanto mais já tenha sido construído o software mais será complexa a sua alteração.

As técnicas de orientação a objetos nos ajuda a mitigar este problema tornando o software mais flexível a mudanças, tornando ao final financeiramente mais barata a modificação do produto em etapas tardias do projeto. Permitindo ser usada em projetos de larga escala e de grande importância na geração de valores.

Abaixo há um gráfico que explica a curva de complexidade dos 3 padrões.

captura-de-tela-2017-05-08-as-20-26-00

 

Técnicas e Ferramentas Propostas

1). Análise orientada a objetos e padrões de projetos

Como mencionado na descrição do Domain Module, primeiro é modelado o domínio em uma representação UML, utilizando a linguagem do domínio como referência onde os substantivos se tornam as classes e os verbos os casos de uso. A interação entre as classes e os métodos das classes podem ser especificadas por diagramas como os de sequencia em UML. Porém com minha experiência não o recomendo, porque dai surge um conflito entre o que o analista pensa ( Ou muitas vezes chamado de Designer) da realização do caso, e o que o desenvolvedor pensa em como resolver o caso. Neste caso o desenvolvedor sempre terá a melhor visão de COMO fazer, e o analista sempre terá a melhor visão de O QUE FAZER.

A solução para este impasse para mim sempre foi o uso de diagramas de atividades, onde são melhor detalhados em especificações de casos de uso.

Claro que recomendo essa pratica para casos complexos que exigem muito feedback do especialista do domínio que muitas vezes é o próprio cliente. Para casos com pouca interação ou complexidade poderia ser simplesmente resolvido com Story case. Notem que esta linha de raciocínio é de simplificar o máximo possível, caso seja um problema complexo recomenda-se um estudo detalhado de caso com estas ferramentas da analise orientada a objeto que se baseiam em especificações detalhadas e representadas em diagramas UML para melhor entendimento.

Tenho um texto publicado que detalha esse processo baseado no estudo de Craig Larman no seguinte link: https://www.academia.edu/7933694/Processo_de_Desenvolvimento_Iterativo_de_Software

O texto do link não trata o uso pratico das tecnologias, porém esta falta será suprida com esse artigo.

2). Tecnologia proposta

  1. Java – Linguagem amplamente difundida no mercado, multiplataforma e ajusta muito bem ao paradigm de orientação a objetos.
  2. Hibernate – Ao invés de criar DAOs , proponho o uso de uma camada de acesso a dados com todos os objetos do modelo de domínio mapeados aplicando técnicas mais a seguir.
  3. MySQL – Escolhida esta base porque neste exemplo não iremos demandar tantos dados, porem poderia ser perfeitamente substituído por qualquer outro motor de base de dados ACID já que toda a arquitetura ira lidar com a logica e controle de transações.
  4. Maven
- Auxilia para gerenciar as dependências.
  5. Tomcat – Servidor WEB que executa nossa aplicação JAVA e Spring
  6. Wicket
- Framework JAVA que trabalha com o conceito de programação por componentes, se encaixa muito bem com quem programa orientado a Domínio utilizando POJOs.
  7. Spring
- Nos será útil por permitir a injeção de objetos on the fly permitindo também a Ioc (Inversão de controle)
  8. AWS
- Amazon WEB services, que prove um computador na nuvem para executar nossa pagina.
  9. RDS
- Banco de dados também oferecido pela Amazon, um perfeito exemplo de DaaS.
  10. Cytoscape – Framework javascript que apresenta dados em grafos por meio de Jquey, útil para alguns casos na camada de apresentação.

 

3). Aplicação de exemplo

Como suporte a este artigo, temos nos servidores da classeasoft um exemplo simples de aplicação prática destes conceitos. Se trata de um sistema gestor de chamados, o mesmo que JIRA Mantis e etc. O problema proposto aqui para que se resolva mediante estas tecnologias é que o workflow seja totalmente flexível. É dizer que o usuário pode definir o circuito de estados de chamados da forma que ele quiser. Como por exemplo: Aberto, proposto, em desenvolvimento, em teste e solucionado. Permitindo configurar todo o fluxo e se necessário voltar a etapas previas ou saltar estados.

No momento em que desenvolvi este sistema vivia na Argentina, por esta razão podem notar alguns comentários em Espanhol, tentarei explica-los todos aqui mas caso tenham alguma duvida podem me escrever perguntando.

 

Acesso ao Administrador do site:

user: adm@nihil.com

pwd: adm

 

3.1) Explicando o Modelo de Domínio   

Diferentemente do padrão Table Module, criamos um diagrama de modelo de domínio para representar a aplicação ao invés de iniciar com um diagrama de entidades relação de base de dados. Partindo do principio de substantivos e verbos podemos criar classes e traçar as relações entre elas.

Abaixo podemos conferir o diagrama de domínio da aplicação.

model

Aqui estão todos os elementos necessários para a solução com suas representações e relações, porém há um elemento a mais que representa o sistema em si mesmo. Note a classe ItemSys que contem como atributo listas de outros elementos dos sistemas, o que seriam os dados mestres em outras metodologias. Isso se deve ao uso de um padrão de desenho de arquitetura chamado RootObject. Que podemos entrar em mais detalhes sobre ele explicando seus benefícios e suas desvantagens.

A principal dificuldade que eu vi na aplicação do Domain Model por parte de desenvolvedores que vinham usando outros padrões, é a indefinição de como usar um modelo de objetos onde os dados são representados em instancias de classes ao invés de linhas de uma tabela.  Tecnicamente falando seria o impasse de tentar usar o Domain Model e Data Row gateway que é algo que não funciona bem devido ao que chamamos diferença de impedância.

A solução para esse caso seria ou armazenar os objetos tal qual estão na memoria em representações de grafos no disco, ou simplesmente mapear a relação entre os objetos e os registros no banco de dados. Surge ai então o padrão Data Mapper que dá origem à ferramentas de ORM como o Hibernate que lida com este problema.

Porém como já sabemos, quando criamos uma nova solução também podemos criar novos problemas. E ao utilizar o DataMapper temos que pensar em como controlar os limites de transação das operações. Por exemplo: Interatuando entre diferentes objetos em um processo notamos que ocorre uma exceção esperada ou inesperada, como saberemos o ponto que devemos fazer rollback e reverter as modificações de dados ocorridas até então?

No caso de estar utilizando o Transaction Script ou até mesmo o Table Module, seria muito mais fácil porque a execução estaria em blocos de códigos procedurais, no caso do Domain Model podemos utilizar o RootObject para isso. O Root object é o objeto raiz que deve representar o sistema em si, e raiz porque ele é também a raiz da representação de um grafo.

captura-de-tela-2017-05-08-as-20-35-14

O padrão Root Object será a base de outra técnica que posteriormente irei mostrar chamada de persistência por alcance. Que basicamente controla o limite de transação tendo em conta os objetos afetados na estrutura do grafo.

captura-de-tela-2017-05-08-as-20-35-28

3.2). Explicando a estratégia para tornar a arquitetura flexível para cloud computing.

Retomando ao tema original, nosso objetivo é tornar a arquitetura flexível o suficiente para escalar em outros servidores, outros repositórios de dados e se integre a diferentes serviços ou até mesmo prover serviços para que aplicações mobile a consumam, que atualmente é o que tende a ocorrer. Com o modulo de persistência resolvido com o root object falta então abstrair a camada de dados e de serviços. Abaixo segue um diagrama de pacotes que busca representar toda a estrutura.

captura-de-tela-2017-05-08-as-20-35-52

Note que a aplicação web Wicket, representada pela classe mais acima, somente tem acesso aos pacotes de repositórios, serviços e DTOs. Essa foi a forma de separar as camadas e permitir escalar em vários tipos de serviços e repositórios de dados. Claro que somente a separação de pacotes não nos dá esta flexibilidade, por isso criamos a classe RepositoryLocator e ServiceLocator. Estes 2 na verdade nada mais são que classes que armazenam as instancias de serviços e repositórios, permitindo também a injeção de dependência.

 

Injeção de dependências (Inversão de controle)

Esta técnica permite injetar de forma dinâmica um serviço ou um repositório de dados sem impactar no funcionamento do cliente ou do modelo.  Esta aplicação de exemplo trabalha com base de dados MySQL e com serviços representados por uma classe. Caso queira instalar esta mesma estrutura em um outro ambiente onde usa serviços SOA, CORBA ou etc, podemos adaptar o sistema e injeta-lo mediante a uma configuração XML do Spring. O mesmo para o repositório de dados, onde podemos sem muito impacto usar múltiplos bancos de dados independente da tecnologia sem impactar no modelo. Pois o modelo como podem ver no diagrama esta na parte mais abaixo somente sendo consumida pela implementação de repositório ( que neste caso o Hibernate) e serviços. Um caso clássico é escalar múltiplos banco de dados para uma mesma aplicação por meio do cloud computing sendo que o esforço de código seria mais que nada adaptar um novo modulo e injeta-lo no locator. Assim como também outros serviços integráveis. O segredo na realidade é que o modelo e o cliente estão devidamente separadas por uma camada de abstração.

No nosso exemplo, no código vocês podem conferir a seguinte configuração no arquivo de configuração do Spring que direciona as injeções de dependência em detalhe.

captura-de-tela-2017-05-08-as-20-36-54

Esse arquivo define que um bean criado pelo método getInstance() tenha como valores de seus atributos as seguintes instancias de classes assim como definida em cada propriedade.

Logo podemos acessar a estes serviços pela camada de apresentação.

captura-de-tela-2017-05-08-as-20-41-47

Vejam que o método adicionar estado pelo serviço que é chamado pela camada de apresentação através do ServiceLocator e logo se recebe um Set de dados como resposta classificado como DTO( data transfer objects) que são nada mais que os valores de objetos de domínios que chegam até a camada de apresentação.

A mesma coisa seria uma aplicação mobile que poderia consumir um serviço como esse, porém através de um WEB service. Através desta técnica seria mais transparente a alteração para escalar o acesso ao modelo de domínio.

Proxy Pattern

Este padrão por essência, trabalha com a representação de um objeto sem interagir diretamente com ele. Para o nosso caso que estamos buscando soluções para abstrair a camada de dados nos ajuda. A equipe do Hibernate notou a utilidade deste padrão e aplicou no seu produto permitindo o uso do LazyLoad, que nada mais é que trazer os dados solicitados de um determinado conjunto de classes sem ter que trazer necessariamente todas as instancias de todas as propriedades. No arquivo applicationContext.xml que esta na pasta WEB-INF, pode-se notar as configurações do hibernate para o mapeio das classes do domínio com as tabelas. O valor padrão do atributo lazy é verdadeiro portanto não é necessária nenhuma ação direta para este caso.

 

captura-de-tela-2017-05-08-as-20-42-56

Decorator Pattern

O Padrão decorator permite acrescentar comportamentos pre definidos a um objetos de forma dinâmica. Outro padrão do livro do Gang of Four que foi parar nos principais frameworks de desenvolvimento de software, como neste caso o Spring.

Por meio do reflection o Spring instancia os objetos que configuramos e armazena onde queremos, que no nosso caso é o ServiceLocator. E a equipe do Spring foi inteligente o suficiente para perceber que se “decorar” uma operação dentro de um serviço com um gestor de transação, pode-se então trabalhar com persistência por alcance, como havia mencionado na secção de root object.  Porque isso conseguimos quando decoramos os comandos de transação( tx.begin(), tx.commit() tx.rollback() e etc) injetando eles sobre um bloco de código. Essa tecnica será melhor descrita no item a seguir.

Unit of Work com Spring

Sendo que, todos os objetos do modelos estão abaixo do root object. Se qualquer objeto deste grafo for modificado, o hibernate marca o root object para alteração assim como todos os objetos subsequentes. Sendo assim, aliando este root object com o gestor de transação do Spring e hibernate, podemos fazer o que chamamos persistência por alcance com características ACID.

Basicamente dentro do arquivo de contexto do Spring configuramos o mapeio.

captura-de-tela-2017-05-08-as-20-44-05

Logo configuraríamos o gestor de transações do Spring para os nosso serviços.

captura-de-tela-2017-05-08-as-20-44-20

O fato de configurar Propagation_Required_NEW como atributo no gestor de transação permite o uso do bloco de código na transação e sempre pede para que uma nova transação seja criada.

Utilizamos o nível de isolamento padrão do banco de dados e temos uma arquitetura preparada para receber nosso modelo de domínio inteiramente representado em objetos, colocando no nível de controle de transação no momento de chamar o serviço seja pela camada de apresentação com o wicket ou qualquer outra fonte que esteja consumindo o nosso service locator.

captura-de-tela-2017-05-08-as-20-44-34

 

Conclusão

Podemos notar que na literatura da engenharia de software há soluções para se trabalhar com Domain Model, mais comumente usado hoje como DDD (Domain Driven Design). Que permite reduzir a complexidade do esforço de modificação a longo prazo e a ajuda do Ubiquitous Language na comunicação da equipe impactanto positivamente em todas as disciplinas da engenharia de software como o teste por exemplo. O sistema publicado no link prototipo.classeasoft.com é uma prova cabal que SIM! É possível trabalhar com esses princípios e o único impacto de performance nisso seria o dos próprios joins nas queries do motor de base de dados. Ao contrário dos que dizem que  DDD seja lento, com os repositórios abstraídos pode até mesmo trabalhar com alguns casos de uso na memoria e ao final persistir nos repositórios RDBMS ou até mesmo usar nesta camada outros frameworks de dados mais orientados a NoSQL como datanucleos ou até mesmo Neo4J  ou DB4o e Versant que são bases de objetos.

Convido a todos(as) que leem este artigo a participarem também do projeto, na pesquisa sobre tudo do Wicket para gerar uma versão 2 mais atrativa visualmente e também com mais repositórios como NoSQL por exemplo.

Mandem um email a marc@classeasoft.com e sigamos com este projeto de caráter OpenSource.

 

Bibliografia

Gamma, Eric, Helm, Richard, Johnson, Ralph, Vlissides, John. Design patterns, elements of reusable object-oriented software. Addison- Wesley. 1995;

Craig Larman, 2007, “Utilizando UML e Padrões”, 3ª ed., Bookman;

Eric Evans, 2010, “Atacando as complexidades no coração do software”, Alta Books

PEZZÈ, M.; YOUNG, M.; Teste e Análise de Software. Porto Alegre: Bookman, 2008;

COCKBURN, A.; Escrevendo Casos de Uso Eficazes – Um Guia para Desenvolvedores de Software. São Paulo: Bookman; 2005 ;

Fowler, Martin. Patterns of enterprise application architecture. Addison – Wesley. 2002;

FOWLER, M. UML Distilled: A Brief Guide to the Standard Object Modeling Language, 3rd Edition, Addison-Wesley, 2003;

Persistencia orientada a Objetos – Mg Javier Bazzocco – Editora UNLP – La Plata 2010;

 

Online

Hibernate. <http://www.hibernate.org>

ODBMS. < http://www.odbms.org>

Db4o. <http://www.db4o.com>

Spring framework. <http://www.springsource.org>.

Versant. <http://www.versant.com>

Wicket. <http://wicket.apache.org >.

AWS <http://aws.amazon.com>.

captura-de-tela-2017-05-08-as-21-17-46