Arquivo

Archive for the ‘Patterns’ Category

Existe vida fora do DDD ?

Não, a interrogação não é sobre se existe vida fora da sua área de discagem… A pergunta é se é possível viver sem usar Domain Driven Development.

Hoje em dia parece-me que muita gente tem a sensação de que se não estiver usando DDD não sabe programar. Ou que, se não usar todas os conceitos/padrões do DDD certinho não é filho de boa gente.

Sendo que o DDD não é nada mais que um novo nome para Orientação a Objetos feita com responsabilidade e qualidade, não me parece que seja certo achar que DDD é a pedra filosofal. Afinal Orientação a Objetos feita com responsabilidade e qualidade é que é a pedra filosofal que pode transforma a sua aplicação de coisas sem valor, num software de primeira linha.

Desde cedo criei uma certa aversão ao chamado “argumento de autoridade literária”. Basicamente, pessoas sem imaginação, sem cultura ou sem entendimento das questões utilizam as palavras que leram em livros – claro que retiradas do contexto – para afirmar o que elas querem. Esta técnica é muito usada pelo pessoal que vê na Biblia Sagrada a resposta para todas às questões, mas também é usada em outros meios. No meio científico, por exemplo, se você não tem um paper sobre o assunto ou não sabe citar um, você não pode se pronunciar. Quantos avanços, na realidade, foram feitos exactamente por aqueles que não citaram papers de ninguém (como poderiam? – era um assunto novo!).

Nas filosofias de desenvolvimento (e existem muitas, talvez demais: TDD, DDD, FDD, BDD, MDD,…) escolheu-se pegar um item necessário ao processo de desenvolvimento , endeusá-lo e fazer tudo girar à volta disso. Em TDD são os testes, em FDD as features, em MDD o modelo, em BDD o comportamento, em DDD o Domínio.

Isso é tudo muito bom até você entender que, na realidade, não faz sentido usar cada um por si. Em um projeto real você precisa de tudo: modelo, domínio, testes, modelo, features, comportamento,… Bom, mas isso é o velho e bom OO que já conhecíamos. Conhecíamos mesmos? Se conhecíamos como é que nos deixamos levar nessas ondas de *DD ?

O que o DDD tem afinal de tão diferente? A resposta é simples: publicidade. Aliás, como a maioria dos *DD e outras filosofias atuais, é apenas um apanhado de melhores práticas já previamente existentes “vendidas” como algo novo que pode salvá-lo da forca. Muda-se um nome ou outro, se esclarece um conceito ou outro, simplificam-se alguns passos e fica tudo bem simples para poder ser explicado em palestras rápidas sobre o assunto de forma que as pessoas achem que aprenderam algo diferente daquilo que sabiam

A verdade é que se você aprender OO como deve ser. Souber usar herança, polimorfismo, modificadores de acesso, e aplicar o Princípio de Separação de Responsabilidade (SoC), e tiver alguma experiência, consegue sozinho chegar nas mesmas conclusões. Padrões de projeto são por definição coisas que se repetem. Lá pela segunda ou terceira vez você vai identificar o padrão, mesmo que não saiba como ele se chama.

O conceito que a DDD lança na mesa (e diz que é novo e sagrado) é um tal de Ubiquous Language (literalmente: “Linguagem Ubiqua”, que significa “linguagem que está presente em todos os lugares”, omnipresente) que basciamente significa que todas as pessoas envolvidas no projeto, desde desenvolvedores a clientes devem usar as mesmas palavras para se referirem aos mesmos conceitos. Isto é tão obvio que dói criar um nome para isso. É uma simples regra de comunicação que se aprender na sexta série , sei lá … Só é possivel comunicar quando o símbolos (palavras) significam o mesmo para todos os intervenientes na comunicação. Protocolos fazem uso desta regra desde sempre. As próprias línguas usam esta regra. Não é nada de novo. O novo é chamar a atenção para a importância disso.

Todos os *DD elevam alguma característica do desenvolvimento ou alguma artefato ou ambos a um estatuto maior (testes, domínio, linguagem, modelo, etc…) e por isso parece que ninguém usava isso antes. Por outro lado os *DD têm a sua própria filosofia para o uso desses artefacos/idéias que já existem, mas que, se não forem usados como a filosofia requer, o resultado será desastroso (mais ou menos como ” se você não seguir a palavra de Deus, o mundo acaba” ). Então, TDD requer que se use testes automáticos (que já existiam antes) acima de todo o resto (ou melhor, antes de todo o resto), MDD requer que se tenha um modelo hiper completo que a partir dele possa ser gerado código automaticamente, e DDD requer que se defina o Domínio. Sendo que o domínio é um conjunto de conceitos que o cliente conhece, é preciso capturar esses conceitos e comunicar com o cliente nessa linguagem “de domínio”, a que o DDD batizou de linguagem ubiqua.

O TDD não suporta o uso de desenvolvimento sem testes, e o MDD não suporta sem modelo, o DDD não suporta nada além do domínio, assim vai. Na realidade você precisa de todos para que o projeto flua.

Você precisa do dominio para levantar do cliente o que aplicação deve fazer, mas ha mais coisas além do dominio. Vc precisa de features (FDD) porque existem dois tipos – funcionais e não funcionais – e o dominio só cobre as primeiras. Mas juntar tudo vc precisa de um modelo, mas não tão completo ao ponto do codigo ser gerado magicamente. Afinal desenvolvedores tb programam… vc precisa de testes para saber se está tudo em ordem … e assim vai. Vc precisa de todas as partes. Cada uma é mais util para um certo fim, mas cada uma sozinha, não leva ao sistema completo.

Bom, então, existe vida além do DDD? sim, há um monte de outros *DD por aí, mas menhum é suficiente. É preciso usar todos em conjunto, ou seja, as melhores práticas de cada um. Entretanto, note-se que as melhores práticas de cada um, são as melhores práticas de desenvolvimento orientado a objetos (OOD).

Em outro prato da balança temos os padrões de projeto referidos em literatura do DDD. Todos existiam antes do DDD ser criado. Talvez alguns ainda não tinham sido “batizados” ou simplesmente eram derivações de alguns outros que já existiam, mas não há nenhuma criação do zero aí. A criação é o isolamento e delimitação, e em última análise, a definição do domínio, através do uso desses padrões.

Quando falo que não é possível fazer DDD sem, digamos, um Repositório, alguns acham isso um absurdo, afinal o “domínio” não é um objeto e o mais importante na DDD é a linguagem ubiqua e não os objetos ou a sua implementação (afinal DDD é uma filosofia, não uma prática, as implementações dos objetos no código não importam nada, SoC não importa nada, etc.. absurdo). Mas sem o repositório, como eu isolo o domínio de, por exemplo, a infraestrutura? A resposta é: não isolo. Se fosse possível, o Repositório não seria mencionado na literatura (dah!). E eu quero isolar? sim. Sem o isolamento não há uma representação do domínio no código o que viola o próprio conceito de Domain Driven e o conceito de linguagem ubiqua, já que os conceitos estão em todo o lugar exceto no código. O exemplo simples é o famoso DAO. Você vai explicar o que é um DAO para o cliente? Ele vai lhe pedir que implemente um DAO? Não. Então um DAO não é um objeto do domínio. O cliente vai apresentar o conceito de “lugar onde as entidades estão”. normalmente a frase usada refere-se a banco de dados porque o cliente acha que é essa a representação do conceito de “lugar onde as entidades estão”. Ele fala: “então aí, depois de ter feito essa conta, você salva no banco”. O que ele quer dizer é “depois de ter feito a conta você guarda no lugar onde as entidades estão“. Este conceito é usada ad naseum e portanto é necessário arranjar um nome para ele (seguindo a permissa da linguagem ubiqua). Então chamamos-lhe Repositório. Algumas vezes o cliente vai dizer “então, você pega do banco todos os produtos dessa marca e mostra na tela com um sistema de navegação paginado” o que ele quer dizer é “então, você pega do repositório todos os produtos dessa marca”. Se o repositório é um banco de dados ou não, na verdade não interessa ao cliente. Se interessar isso é um requisito não-funcional porque a implementação do repositório não altera a funcionalidade do domínio.

Bom, mas podemos chegar no conceito de repositório de outra forma. De uma forma OOD.
É comum que o sistema faça pesquisas por dados. O SoC exige que apenas um objeto saiba onde esses dados realmente estão e que um objeto seja responsável por montar as pesquisas. Do ponto de vista dos outros objetos, ele chamarão o objeto que faz as pesquisas e esse invocará o objeto que sabe onde os objetos estão. Do ponto de vista exterior, apenas existe o objeto que faz as pesquisas, já que o outro não pertence à mesma camada (pertence a uma camada inferior), portanto, para a camada que quer os objetos, tudo se passa como se os objetos estivessem contidos dentro do objeto que faz as pesquisas e retornas os objetos. Por coerência esse objeto é chamado de Repositório. Este é o padrão proposto fora do DDD por Martin Fowler e depois recauchutado para o conceito de Repositório usando na literatura do DDD (como disse, nada foi criado).

A conclusão é que, seguindo apenas um desenvolvimento OO simples, sem utilizar as filosofias especiais do DDD, chegariamos na mesma conclusão. Talvez não tão depressa, mas chegaríamos. Isto é óbvio, porque OO é uma teoria fechada, não ha como inventar nada fora dela sem a alterar.

Bom, então existe vida fora do DDD ? Sim, existe o desenvolvimento orientado a objetos.

Como todo o desenvolvimento ele está vigiado por testes, montado em cima de modelos, deve prover features, e valor para o cliente/ usuário do software. Além disso deve prover qualidade e fácil manutenção para outros desenvolvedores. Nada de novo aqui (!).

Então a minha sugestão é que não se perca em *DD antes de ter uma muito boa base em desenvolvimento orientado a objetos e em desenvolvimento per se. Se sentir-se tentado a dar uma espiada, ótimo, mas tenha em mente que não existe nenhuma revelação exotérica nessas filosofias.

Menos conversa e melhor modelagem OO. É só isso que é preciso.

Arquitetura sem DAO

Uma das pragas dos sistemas escritos hoje em dia é o uso abuviso e inconsciente de objetos intitulados DAO. Se perguntar ao criador do objeto DAO por quê ele precisa deste objeto no sistema ele responde que é para isolar a camada de persistência. Se isso fosse verdade o objeto implementaria o padrão Façade ou Strategy e não o padrão DAO.

O padrão DAO é importante em sistemas orientados a banco de dados. O problema é que muitos dos sistemas de hoje ainda são orientados a bancos de dados, mas os seus desenvolvedores auto-enganam-se até pensar que não são. Apareceu a moda dos sistemas orientados ao domínio. E quem não quer estar na moda ? Afinal o que está na moda sempre é mais caro…

O maior precursor dos sistemas orientados ao domínio foi o filosofia de Desenvolvimento Orientado ao Dominio (Domain Driven Development – DDD, em inglês). Nesta filosofia o desenvolvimento é guiado pelo entendimento e re-estruturação (refactoring) constante do Dominio. O resto da aplicação utiliza o dominio como uma caixa-cinzenta (ou caixa-negra da qual sabemos algumas coisas) e responsabilidade real do funcionamento é do domínio. O resto é apenas suporte para ligar as partes do sistema com o domínio.

Estamos perante uma Paralaxe de Paradigmas. Por um lado o paradigma do uso do DAO como peça importante na comunicação com banco de dados e pivot em sistemas orientados a bancos de dados. Por outro lado o paradigma da orientação ao dominio com uma riquesa inerente em cada entidade. No meio, a necessidade inerente dos sistemas de preservar seus estado entre reboots e o uso de banco de dados para persistir esses dados. A paralaxe está no seu auge quando sistemas orientados a banco de dados são encarados, explicados e vendidos como sistemas orientados ao dominio, mas onde as entidades são pobres e o dominio não passa de um nome bonito para mais uma fachada de persistência em banco de dados.

A Síndrome de DAO está instalada na mente de muitos por não conseguirem se elevar além do topo da onda da paralaxe causada pelo choque entre a orientação a banco e a orientação ao domínio. Todo o munda usa o objeto DAO, mas nenhum desses objetos é realmente uma implementação do padrão de projeto Data Acess Object já que hoje quase todos eles são implementados com base num DomainStorage (personificado pelo Hibernate ou por uma implementação do JPA).

A separação de responsabilidade do domínio e do mecanismo de persistência é uma necessidade se você não quer implementar mecanismos de persistência cada vez que faz um sistema. Para isso nasceu o padrão DomainStore. Mas mesmo assim as pessoas insistem em encapsulá-lo em um DAO. Me ultrapassa a compreensão de por quê alguem faz isto. A única conclusão que posso tirar é que a pessoa não é consciente do mal que está fazendo.

Tendo em atenção que – com base no que vinculado na imprensa e blogs por ai fora – o paradigma da orientação ao dominio não é entendido no seu cerne sendo confundido com os principios da filosofia do Desenvolvimento Orientado ao Domínio. Parece-me que cabe esclarecer como é a arquitetura de um sistema em que não se criam objetos DAO, nem seja orientado a banco de dados.

Claro que, se o seu sistema é orientado a banco de dados esta arquitetura não se aplica.

Categories: Desenvolvimento, Java, Patterns

TDD e Design Patterns

Uma das críticas mais importantes ao uso de Design Patterns (Padrões de Projeto) é o exagero. Muitas gente se revolta com o exagero de colocar padrões em todo o lugar do código. Isso é conhecido como sobre-engenharia.

Por outro lado, umas das práticas que mais vem criando adeptos é a construção de testes para o código. Seja seguindo uma disciplica específica como TDD ou algo mais casual, o ponto é que o código tem que ser testado.

O argumento que quero apresentar é que se utilizar padrões de projeto corretamente a construção dos testes é simplificada e vice-versa; para poder criar bons testes as classes têm que seguir bons padrões de projeto.

Essa simbiose é tão mais importante quanto mais “de integração” forem seus testes.

Imaginemos que queremos testar uma classe de processo. Este processo faz uma pesquisa no repositório, trata os dados criando algumas instâncias de objetos que ele persiste nesse mesmo repositório. Para que possamos fazer o teste temos que garantir que o processo encontra instâncias pré-determinadas, as processa e gera outras instâncias cujo estado é também pré-determinado. Digamos que o processo irá ler as instâncias A, B e C e produzir D. Para testar o sucesso do teste temos que ser capazes de ler D.

Poderiamos utilizar um repositório que conecta com o banco e ler D do banco. Contudo, estaremos também testando a integração com o banco. Além disso, precisaremos para começar de um banco de dados. Como existe uma comunicação (provavelmente em rede) o teste é demorado. O ideal seria fazer tudo sem necessitar de um banco de dados.

Se o repositório foi construído seguindo o padrão Repository podemos utilizar estratégias diferentes para o processo de teste construindo pesquisas em memória em vez de em banco. Isso é simples se utilizarmos o padrão DAO junto com QueryObject. Basta-nos criar um DAO que manipula listas em memória e não teremos mais que utilizar o banco de dados. Esse DAO pode ainda prover formas de testar a presença de objetos de forma a facilitar os testes.

Um outro exemplo simples é quando um certo processo depende de uma hora ou data. Você não vai esperar que a data chegue para testar o código , nem vai alterar a data da sua máquina. A solução é utilizar um Façade/Strategy na forma de uma interface Clock. Essa interface retorna a hora e data atual. Com isso você pode fazer o teste implementando um Clock em que você passa uma hora e data e ele sempre retorna essa (StoppedClock). No sistema real, você usa um Clock que lê a data e hora do sistema ( SystemClock). Nada se usar “new Date()”. Pode-se ainda criar um relógio que avança o tempo mais depressa que o normal (SpeedyClock) de forma a testar mudanças de estado que acontecem com a passagem de tempo.

Quanto mais corretamente for a engenharia (o design) do seu projeto, mais facilmente você encontra pontos de substituição para poder controlar os testes de maneira fácil, usando objetos que emulam o comportamento esperado ou permitem controlá-lo de forma mais simples.

Um sistema sem padrões de projeto não resiste a tentativas de teste porque é complexo desanexar as partes para que sejam testadas independentemente. O efeito disso é abandonar o teste, quando na realidade o efeito deveria ser introduzir os padrões necessários.

Adicionar Padrões de Projeto no seu sistema é um exagero? Não quando você precisa deles para testar o código.

Wire It to me

Como saberão estou – no meu pouco tempo livre – envolvido no projeto MiddleHeaven. Umas das capacidades mais interessantes do Middleheaven é ganhar independência de plataforma através do uso de serviços. Como comentei antes, “serviço” é uma modelo que está em alta e que parece fornecer encapsulamento, isolamento e desacoplamento por definição. Adicionamos a isso a programação para interfaces e os mecanismos de meta-programação (reflection, bytecode-rewrite) e temos um mecanismo muito poderoso para adicionar funcionalidade no sistema.

Ao trabalhar com serviços existem algums detalhes que precisam ser suportados por um framework. Primeiro, tem que existir uma forma de obter o serviço. Tudo bem que não sabemos qual a implementação do serviço – e não nos interessa – mas temos que a obter em algum momento. O padrão básico para isto é o Registry. A idéia é que a implementação é registrada “antes” e “depois” o serviço é recuperado. Esta mecânica existe em vários frameworks baseados em serviços como JMX ou OSGi. Este simples requisito (obter a implementação) é mais complexo do que parece. Acontece que um mesmo serviço pode funcionar diferentemente conforme algum parâmetro e podemos estar interessados em usar mais do que um serviço do mesmo tipo. Por exemplo, um serviço de dicionário tem vários tipos: um para cada língua. Ao pegar o serviço a interface não basta. É preciso (algumas vezes) adicionar algums parâmetros na busca. Dessa forma, é possivel dizer “obtenha a implementação do serviço dicionário para o francês” e depois repetir “obtenha a implementação do serviço para o inglês” e com isso construir um código que testa se uma certa palavra é homografa entre as línguas (como por exemplo weekend). Um mecanismo baseado em serviços tem que ser capaz de suportar diferentes “sabores” de um mesmo serviço. Até aqui parece simples. Basta passar os parâmetros durante a procura. Mas quais parâmetros são permitidos para cada serviço? Precisamos saber? Versão é um parâmetro?

O outro ponto é que usar um Registry para obter os serviços torna o código dependente do próprio registro e da forma de o acessar. Um mecanismo melhor seria usar injeção de dependência. Mas para ser realmente prático, uma implementação de um mecanismo destes teria que ser feito com base em anotações (compare Guice com Spring e veja a diferença entre usar anotações e XML). Isso é interessante porque não mais tenho que me preocupar com o mecanismo de procura , apenas espeficico o que quero. Algo assim:

@Wire @ServiceParams(“language=fr_FR”) Dicionario dicionarioFrances;
@Wire @ServiceParams(“language=en_UK”) Dicionario dicionarioIngles;

O mecanismo de injeção faria algo como

ServiceRegistry.getService(Dicionario.class, serviceProperties )

em que serviceProperties seria um mapa populado pela instrospeção de @ServiceParams.
Um mecanismo de injeção não demora a construir, embora demore a maturar para resolver problemas como referência cíclica e outros. Ou seja, em tese, não é pedir muito que o MiddleHeaven inclua um mecanismo de injeção e permita ao objetos pedir “Hey! MiddleHeaven, wire it to me!”

Tudo muito bem, mas existe ainda um outro fator nesta história. Decidir quais implementações dos serviços devem ser usadas. Esta decisão depende de vários fatores. Serviços de infra como transações e comunicação remota dependem do contexto em que a aplicação corre. Dentro de um container jee é apenas adaptar o serviço provido pelo próprio container, enquanto que em standalone precisamos de implementar o próprio mecanismo ou delegar a alguma biblioteca de terceiros. A aplicação também pode decidir que precisa de um certo serviço ( por exemplo, serviço de reports). Esse serviço tem que estar presente para a aplicação funcionar, mas não é essencial nem utilizado sempre e nem depende do contexto. Além desses, temos serviços externos como serviços de cotação ou B2B que a aplicação usuará.

A este ponto deve estar pensando que já ouvi esta conversa em relação a SOA. SOA é orientado a comunicar sistema fora do mesmo “processo de negocio”. Mas podemos trazer o conceito para dentro e utilizar um design baseado em componentes que funcionam como serviços uns dos outros. Claro que isso é mais facil dito que feito.

Como os serviços necessários à aplicação podem ser adicionadas conforme necessário eles podem também ser removidos ou subtituídos (especialmente os de terceiros) quando necessário. Isto pode significar que depois que a variável foi connectada à implementação, a implementação muda. E muda em runtime. O objeto que a variável aponta não é mais válido. Obviamente isto implica no uso do padrão Observer mas como não há interação com o registro de serviços, o próprio mecanismo de injeção tem que conter alguma lógica especial ou melhor, ser possível injetar alguma lógica especial no mecanismo de injeção ;-) . Ou seja, temos que aliar AOP com o mecanismo de injeção para trabalhar com serviços errantes.

Indo mais fundo no problema, a apliação em si mesma pode ser entendida como um conjunto de serviços. Normalmente chamamos as partes de um aplicação de módulos. A idéia é criar uma aplicação criando um conjunto de módulos. Um módulo “principal” contém o coração da aplicação. O que esse módulo faz (o seu serviço) depende de outros módulos e/ou pode ser estendido por outros modulos.

Um exemplo classico são os ERP. Uma compra gera um conjunto de eventos de negócio. Cada módulo (contabilidade, financeiro, etc..) enxerga esse evento de forma diferente e o processa de forma diferente. Para um sistema básico a implementação do módulo pode simplesmente ignorar o evento ou apenas persisti-lo para tratamento posterior. Um módulo mais avançado não apenas produziria informação com base no evento, como ainda disponibilizaria seus serviços para serem consultados por outros modulos/sistemas (via webservice, por exemplo). Um módulo mais avançado poderia alterar o serviço de UI da aplicação para incluir mais telas que permitem ao usuário interagir com o módulo, e alterar o serviço de permissões para restringir o acesso dos usuários. Os módulos seriam então configuradores de serviços, conscientes dos outros módulos de que eles dependem e fornecendo novos serviços para serem consumidos por outros módulos, aplicações ou sistema.

Esta é a idéia por detrás do MiddleHeaven como um framework orientado ao negócio. A aplicação é apenas mais um serviço, composto por outros serviços. O detalhe agora é saber como colocar tudo isto no mesmo caldeirão e funcionar.

A minha idéia era, como comentei antes, utilizar frameworks como o OSGi ou o ainda não estreado Java Module System para cuidar da localização e registro de serviços, mas hoje parece-me que esse mecanismos podem não ser compativeis e/ou não seguir o caminho necessário para o MiddleHeaven. Ou serem mais abrangentes que esse caminho. Enfim, abstrair esses mecanismo parece uma boa idéia para manter a independência do MiddleHeaven mas parece complicado demais…

Padrões

Uma das mais inimitáveis características do ser humano é ser capaz de reconhecer padrões. Não apenas padrões de cor, forma ou som, mas também padrões em idéias, conceitos e abstrações. A matemática não seria possível sem essa capacidade.

Venho elaborando descrições do padrões mais importantes (a lista não pode ser exaustiva porque existem bastantes ) em orientação a objetos em java hoje em dia. Claro que a implementação é naturalmente orientada para java, mas os padrões em si mesmos são abstratos e podem ser implementados em qualquer linguagem O.O. ( a maioria, pelo menos).

Os padrões são resultado da estricta e repetida aplicação do Principio de Separação de Responsabilidade e são, na realidade, uma expressão utilitária desse principio.

Espero apresentar uma vasta gama de padrões (pelo menos os usados no MiddleHeaven) mas não é um trabalho rápido devido ao imenso numero de padrões que existem.

Categories: MiddleHeaven, Patterns
Seguir

Get every new post delivered to your Inbox.