Seções iMasters
Arquitetura de Informação + Desenvolvimento + Design & UX

Arquitetura evolucionária e design emergente: considerações e técnicas para arquitetura ágil

A arquitetura evolucionária e o design emergente lida com uma variedade de tópicos relacionados à arquitetura evolucionária, incluindo a distinção importante entre design e arquitetura (e como mencioná-los separadamente), alguns problemas que surgem quando se cria arquitetura em nível corporativo e a diferença entre a tipificação estática além da dinâmica em arquiteturas orientadas a serviço.

Se você tem acompanhado a série de artigos que aborda a arquitetura evolucionária e design emergente, você deve ter observado que
passei boa parte do tempo falando de design. Fiz isso por uma série de
motivos.

Primeiro, muitas definições de arquitetura existem no mundo do
software (para o bem ou para o mal), embora o design emergente
atualmente desfrute de menos fama. Segundo, muitas das preocupações em
design têm soluções concretas menos contextualizadas. A arquitetura
sempre envolve muito acoplamento com a infraestrutura física e lógica
das organizações, o que torna muito mais difícil falar disso
isoladamente.

Este artigo corrige a falta de material sobre a arquitetura ágil. Aqui,
eu falo sobre como distinguir a arquitetura do design, abordo algumas
considerações mais amplas sobre arquitetura e toco no assunto do espaço
da arquitetura orientada a serviço (SOA) ágil, com uma discussão sobre a
versão de terminais.

Distinguindo arquitetura de design

A definição de arquitetura de Martin Fowler (a partir de conversas com ele) é a minha favorita:

Arquitetura é aquela coisa que é difícil de mudar depois. E deve existir o mínimo possível dessa coisa.

É possível pensar na interação entre arquitetura e design como o relacionamento mostrado na Figura 1:

Figura 1. Relacionamento entre arquitetura e design

A arquitetura de um sistema de software forma a base de tudo, representada na Figura 1
como caixas cinzas. Os elementos de design ficam acima da arquitetura,
como mostram as caixas vermelhas. Sendo mais fundamentais, os elementos
de arquitetura são mais difíceis de mover e substituir porque será
preciso mover todas as coisas que estão acima deles para acomodar as
mudanças.

Essa distinção facilita a identificação do design em
comparação com a arquitetura. Por exemplo, a estrutura da Web usada é um
elemento de arquitetura difícil de substituir. Entretanto, nessa
estrutura da Web, é possível usar diferentes padrões de design para
expressar metas específicas, o que sugere que a maioria dos padrões de
design formais de fato faz parte do design em vez da arquitetura.

A conclusão para a definição de arquitetura de Fowler é que você deve
construir os elementos de arquitetura para que sejam mais fáceis de
substituir caso seja realmente necessário. Mas como é possível assegurar
isso? Eis um exemplo aqui.

Várias estruturas tentam seduzi-lo a usar algumas de suas classes em
vez das mais gerais que vêm com JDK ou de um elemento de corpo de
padrões abertos (como OASIS). Esse é o modelo de acoplamento “viciador”:
se você se render à tentação, ficará preso à estrutura para sempre. A
abordagem geral dessas estruturas é tornar algo bem mais fácil se você
usar suas classes. Um exemplo perfeito disso vem da estrutura da Web
Apache Struts.

As classes no seu aplicativo que contêm regras de negócios e outros códigos que não são de infraestrutura são as classes de domínio:
elas têm as informações interessantes sobre o seu domínio com problema.

Uma das classes auxiliares excelentes inclusas em Struts é a ActionForm. Se você herdar os objetos de domínio de ActionForm,
todos os tipos de mágica ocorrerão no seu aplicativo. Você obtém
preenchimento automático de campos de formulário a partir de parâmetros,
validação automática (nas camadas de servidor e da Web) e outras coisas
que oferecem praticidade.

Tudo o que precisa ser feito é definir a
classe ActionForm de Struts como subclasse, como mostrado na Figura 2:

Figura 2. Usando a classe ActionForm de Struts

Na Figura 2, a caixa com o rótulo Model tem o objeto de domínio. Ele estende ActionForm de Struts, tornando essa estrutura difícil de ser alterada posteriormente. Se, em algum ponto no futuro, você decidir que ScheduleItem
também precisa funcionar em um aplicativo Swing, você estará perdido.
Você ficará diante de duas soluções igualmente intragáveis: arrastar
todos os Struts para o aplicativo Swing (e não usá-lo) ou se livrar da
dependência de Struts.

A melhor alternativa usa composição em vez de herança, como ilustrado na Figura 3:

Figura 3. Desmembrando sua classe de domínio por meio de composição

Nessa versão, a classe de domínio (em amarelo) inclui uma interface que define a semântica de um item de planejamento. O ScheduleItem original implementa essa interface, que é também implementada por ScheduleItemForm, forçando a semântica das duas classes sempre a concordar.

ScheduleItemForm, por sua vez, tem uma instância do objeto de domínio ScheduleItem, e todos os acessadores e mutadores de ScheduleItemForm passam para os acessadores e mutadores subjacentes do ScheduleItem
encapsulado. Isso lhe permite aproveitar as vantagens dos recursos
excelentes de Struts enquanto você se mantém desmembrado da estrutura.

A regra prática é esta: é bom a estrutura saber de você, mas não é
bom você saber da estrutura. Enquanto for possível manter o
relacionamento, você impede o acoplamento do código à infraestrutura,
permitindo a realização de alterações na arquitetura e no design com
mais facilidade.

Às vezes, é necessário um pouco mais de trabalho para
fazer isso, mas, no final, você tem flexibilidade aprimorada. Struts não
é a única estrutura a oferecer essas economias tentadoras.
Praticamente, toda estrutura tem alguns auxiliares que o vincularão à
estrutura.

Se você alguma vez importar pacotes de uma estrutura ou
fornecedor em suas classes de domínio, você provavelmente estará
arrumando dor de cabeça futura.

Algumas considerações sobre arquitetura

Além da definição de arquitetura, uma grande quantidade de preocupações
surge em configurações corporativas típicas. Mencionarei apenas algumas
das abordagens de arquitetura ágil aqui.

Política de arquitetura

A política corporativa é uma das primeiras coisas desagradáveis que
você encontra quando promovido para a posição de arquiteto. Como o arquiteto
é geralmente a posição técnica mais alta nas empresas, você se torna o
porta-voz (e o defensor) de todas as decisões que ocorrem no
departamento de TI, para o bem ou para o mal.

Na verdade, você leva a
culpa por todas as coisas ruins que acontecem e não leva nenhum crédito
pelas boas. Alguns arquitetos mais novos tentam ignorar isso (que
parecia funcionar muito bem enquanto você estava nas trincheiras
técnicas), mas isso não funcionará mais em sua nova posição.

Lembre-se de que a comunicação é mais importante do que a tecnologia
na maioria dos projetos de software. Se você alguma vez esteve em um
projeto de software que deu errado, considere os motivos de ele ter
falhado. Isso ocorreu devido à tecnologia ou a um problema de comunicação?
Na maioria das vezes, é de comunicação em vez de tecnologia.

Os
problemas tecnológicos têm soluções. (Às vezes, as soluções são
difíceis, mas os problemas tecnológicos têm uma solução.) Os problemas
sociais são muito mais confusos e difíceis de resolver. Uma das famosas
citações do livro Peopleware é:

É sempre um problema de pessoas.

Mesmo para as decisões de tecnologia mais simples, a política sempre
exercerá influência, especialmente se você estiver na posição de aprovar
compras de ferramentas corporativas.

Lembre-se de que, como
arquiteto, você não só precisa tomar decisões importantes, mas
defendê-las também. Às vezes, as pessoas com quem você conversa têm suas
próprias agendas que não fazem sentido logicamente falando, mas fazem
sentido no teste da política corporativa. Não fique frustrado e
lembre-se do porquê da decisão que tomou em primeiro lugar.

Construir versus comprar

Nas questões comuns que surgem em grandes empresas na decisão de
construir ou comprar: para os requisitos atuais, devemos comprar COTS
(Commercial Off-the-Shelf Software) ou construí-lo nós mesmos? A
motivação para essa decisão é compreensível ? se a empresa puder
encontrar algum software já pronto que faça exatamente o que é
necessário, isso poupará tempo e dinheiro.

Infelizmente, vários
fornecedores de software entendem esse desejo e criam um software em
pacote que poderá ser personalizado se não fizer exatamente o que o
cliente precisar. Eles são motivados a criar o software mais genérico
que puderem porque ele provavelmente servirá na maioria dos
ecossistemas. Mas, quanto mais genérico forem, mais personalização será
necessária. É quando um exército de consultores aparece, às vezes
levando anos para concluir o trabalho de codificação personalizada.

A questão de comprar COTS ou não realmente leva a outra pergunta: o processo de negócios é suportado por um software estratégico ou de custo adicional?
A aquisição de COTS fará sentido perfeitamente se o processo de
negócios em questão for meramente um custo adicional. Exemplos desse
tipo de software incluem recursos humanos, finanças e outros processos
de negócios comuns. O software estratégico proporciona uma vantagem competitiva no seu campo de negócios. Essa vantagem competitiva não deve ser descartada de imediato.

O fluxograma na Figura 4 foi projetado para ajudá-lo a decidir entre construir e comprar:

Figura 4. Fluxograma de decisão de construir versus comprar

Nesse fluxograma, a primeira decisão que deve ser tomada toca no
assunto da distinção importante entre estratégico e custo adicional. Se a
necessidade for estratégica, você deverá sempre construir a sua própria solução. Caso contrário, você estará propositalmente se colocando no mesmo nível
de seus concorrentes em vez de criar algo exatamente voltado para as
suas necessidades atuais e futuras.

O software em pacote pede
personalização, mas essa personalização tem limites. Se você criar o seu
próprio software, isso levará mais tempo, mas você terá uma plataforma
em que poderá criar coisas que o diferenciem de seus concorrentes.

A segunda decisão no fluxograma pergunta se o software em pacote é
útil imediatamente. Uma armadilha comum na compra de software em pacote é
não entender exatamente quanto tempo levará para ele se adaptar ao seu
processo de negócios; a maioria das empresas negligencia isso bastante.

Quanto mais você o personalizar, mais tempo ele levará para entrar em
operação. Pior ainda, algumas empresas permitem que seu processo de
negócios mude para acomodar o software. Isso é um erro porque, para o
bem ou para o mal, seu processo de negócios deve ser diferente do
processo de seus concorrentes.

A terceira etapa na árvore de decisão pergunta se o pacote é extensível em vez de personalizável.
Os sistemas extensíveis têm formas bem-definidas de estender a
funcionalidade sem precisar tirar nada do lugar. Esses pontos de
extensão incluem APIs bem-definidas, chamadas SOAP e similares. A
personalização significa que você precisar “trapacear” para fazer com
que o pacote faça o que você quer. Por exemplo, se abrir um arquivo WAR
para que possa substituir um arquivo com o nome index.gif por uma imagem
diferente (que deve ter o nome index.gif), você estará personalizando,
não estendendo.

O teste final é se as suas alterações têm ou não alguma
chance de sobreviver a uma atualização. Se tiverem, você estendeu o
pacote; caso contrário, você o personalizou. A personalização o
desestimula a manter o pacote atualizado porque você percebe quanto
esforço é necessário para se efetuar as mesmas alterações para a nova
versão. Dessa forma, a tendência não é atualizar, deixando você quatro
ou cinco versões para trás da mais recente, o que o coloca em risco de
perder o suporte para a versão antiga que está usando.

O que é custo adicional para algumas empresas é estratégico para
outras. Por exemplo, eu prestei alguns serviços de consultoria para uma
financeira, cujo processo de contratação é considerado uma de suas
principais vantagens estratégicas. Eles contratam os melhores e os mais
brilhantes, gastando bastante tempo e esforço para encontrar a pessoa
certa. Certa vez, eles pediram meu conselho sobre a compra de um sistema
de recursos humanos COTS, e eu desaconselhei isso: por que eles
deveriam se colocar no mesmo nível de seus concorrentes?

Portanto, em
vez de comprar um sistema COTS, eles seguiram meu conselho e
desenvolveram seu próprio sistema de RH. Esse desenvolvimento levou mais
tempo, mas, quando estava pronto, eles tinham uma plataforma que
facilitou as tarefas que, para seus concorrentes, necessitavam de mais
mão-de-obra. A contratação é simplesmente um custo adicional para muitas
organizações, mas, para essa empresa, era estratégica.

Evite a armadilha

Lembre-se: nem todos os processos de negócios podem ser transformados em
bens, e há os que variam de uma empresa para outra. Não caia na
armadilha de acreditar nos fornecedores que dizem já ter uma solução
pronta para o seu processo de negócios. Se eles tiverem, você pode
apostar que estão vendendo essa mesma solução para os seus concorrentes
também.

Tipificação na arquitetura

Um tópico mais técnico (menos orientado a processo) que surge
geralmente nas iniciativas SOA tem a ver com a tipificação e a versão de
sistemas distribuídos. Essa é mais uma das armadilhas comuns nesses
tipos de projetos. Ambas são comuns porque é fácil seguir um caminho
definido pelos fornecedores de ferramentas e porque leva um tempo para
um problema se manifestar ? e os problemas mais difíceis surgem do fato
de não se saber o que você não saber nas etapas iniciais de um projeto.

O debate sobre se devem ser criado ou não sistemas “corporativos” com
linguagens tipificadas dinamicamente é longo, e os argumentos agora são
suficientes. Entretanto, esse debate informa uma consideração
importante para sistemas distribuídos com relação à tipificação dos
terminais. Por terminais, estou me referindo ao portal de
comunicação entre dois sistemas distintos.

Os dois estilos de
tipificação concorrentes são SOAP, que geralmente produz tipificação
forte usando padrões como Web Services Description Language (WSDL) e
Representational State Transfer (REST), que favorece uma abordagem
voltada para documentos tipificada fracamente.
Os prós e contras de SOAP versus REST estão fora do escopo do artigo;
aqui, eu quero principalmente falar sobre os benefícios da tipificação
fraca no nível de terminal, o que você pode conseguir com qualquer um
dos estilos.

A tipificação mais dinâmica é importante em terminais porque esses
terminais formam uma API de integração publicada entre sistemas que
geralmente evoluem em passos diferentes. Você quer evitar assinaturas
específicas de acoplamento firme (tipos e nomes de parâmetros) entre
esses sistemas, o que tornaria cada lado da conversa frágil e propenso a
quebra, dificultando sua capacidade de criar versão dos dois
aplicativos de maneira independente.

Eis um exemplo aqui. Na integração de estilo SOAP tradicional, você
usa um tipo de protocolo RPC, utilizando WSDL para definir os detalhes
da conversa entre os dois aplicativos. Isso está ilustrado na Figura 5:

Figura 5. Usando chamadas de estilo RPC entre aplicativos

A integração de estilo RPC usa WSDL para usar uma chamada de método
“regular” e subtraí-la para SOAP. Dessa forma, cada classe mapeia para
um tipo em WSDL, incluindo os tipos de todos os seus parâmetros. Essa
abordagem une firmemente os dois lados da conversa porque eles contam
com WSDL para definir o que é enviado e o que é esperado.

O problema
está nessa definição estrita. E se você precisar modificar um dos
aplicativos para que sejam usados parâmetros diferentes ou alterar o
tipo de um existente, não sendo possível dessa forma modificar os dois
aplicativos ao mesmo tempo? Como você cria a versão do terminal?
Diversas formas são possíveis, mas todas elas têm sérios
comprometimentos.

Por exemplo, é possível criar outro terminal com a
nova definição de WSDL. Se o terminal original tiver o nome addOrder, você poderá criar outro terminal com o nome addOrder2.
Você pode ver a que incerteza isso leva. Em breve, você terá dezenas de
terminais levemente diferentes, com código duplicado em toda parte,
lidando com uma situação de cada vez porque é difícil de antecipar como
as pessoas usarão o ponto de integração quando ele estiver publicado.

Jogos podem ser executados com resolução de terminal com o uso de
ferramentas como Universal Description, Discovery, and Integration
(UDDI) (ou apenas um mapa de hash), mas a escala não fica boa. O
problema fundamental é o acoplamento forte entre os terminais, o que os
impede de evoluir em um ritmo natural e independente.

Uma abordagem alternativa é trata os terminais de integração como tipificados fracamente, como mostrado na Figura 6:

Figura 6. Usando tipificação fraca em terminais de integração

No envio das informações interessantes ao terminal dentro de um
documento, pode-se deixar a definição de terminal inalterada nas
atualizações grandes e pequenas para qualquer lado da conversa. Em vez
de contar com WSDL para definir de modo convincente o que espera, você
tem a opção de flexibilidade. Agora, o terminal usa um documento que
encapsula os tipos de coisas de que o terminal precisa.

Para manipular a versão do terminal, a primeira etapa de resolução do
terminal é descompactar o documento, determinar o que foi passado e
reconciliar isso com o que é esperado. Eu geralmente uso uma combinação
dos padrões de design Fábrica e Estratégia para determinar se estou conseguindo o que espero, como mostrado na Figura 7:

Figura 7. Descompactando o conteúdo dentro do terminal para determinar os tipos

A primeira tarefa do terminal é olhar o manifesto do documento e
determinar o que ele contém. Em seguida, ele usa uma fabrica para
instanciar a estratégia correta, a fim de extrair essas informações do
documento. Quando todas as partes tiverem sido verificadas (com o uso de
WSDL, se necessário), os objetos desserializados serão enviados adiante
para processamento de negócios.

Inúmeros benefícios aparecem nessa abordagem. Primeiro, é uma má
ideia ter um mecanismo com duas tarefas ortogonais, embora seja isso o
que a RPC tradicional considere: o terminal é responsável por fornecer a
API publicada para integração e para verificar a tipificação. Como ele
tem dois comportamentos, você tende a misturar o código, tornando a sua
compreensão e a sua manutenção mais difíceis.

Segundo, agora é possível
ter qualquer número de usuários nesse terminal, todos usando versões um
pouco diferentes dele. Quando tiver uma estratégia, você poderá oferecer
suporte a qualquer versão (incluindo versões antigas de aplicativos de
atualização lenta) com o mesmo terminal. Isso lhe permite efetuar as
alterações necessárias, e você não precisa se preocupar em forçar o
restante dos aplicativos da empresa a acompanhar as suas alterações.
Eles podem mudar e usar novas versões de documento em suas próprias
escalas de tempo.

Não existe (ainda) nenhuma ferramenta ou estrutura disponível que
permita implementar essa abordagem de maneira trivial, mas um pouco mais
de trabalho proporciona os benefícios já mencionados. É possível
implementar esse estilo usando SOAP ou REST (embora isso seja mais fácil
em REST porque REST é inerentemente voltado para documentos).

Quando se
cria um ecossistema tipificado fracamente, é possível permitir que
grupos de desenvolvimento diferentes sigam no seu próprio ritmo,
permitindo que toda a empresa use aplicativos para seguir adiante com a
menor dificuldade possível. É essa a essência da arquitetura
evolucionária: estabelecer uma base que permita uma mudança sem
dificuldades no ritmo mais rápido possível sem comprometer os recursos.

Conclusão

A arquitetura é um tópico grande e complexo em software; nesta parte, eu
tentei tocar em vários assuntos diferentes, desde política até os
detalhes de implementação para versão de terminais em SOA. Em partes
futuras, mostrarei mais dessas ideias sobre arquitetura em geral e
algumas abordagens de arquitetura novas para criar uma SOA que possa ser
expandida sem a necessidade de pagar milhões a fornecedores.

Recursos

Aprender

artigo publicado originalmente no developerWorks Brasil, por Neal Ford



Neal Ford é um arquiteto de software e Meme Wrangler, na ThoughtWorks,
uma consultoria global de TI. Projeta e desenvolve aplicativos,
materiais de instrução, artigos para revistas, treinamentos e
apresentações em vídeo/DVD, e é autor ou editor de livros que abordam
uma variedade de tecnologias, inclusive The Productive Programmer
Seu enfoque é o projeto e construção de aplicativos corporativos de
grande porte. Também é orador internacionalmente aclamado nas
conferências de desenvolvedores ao redor do mundo. Conheça seu Web site.

Mensagem do anunciante:

Torne-se um Parceiro de Software Intel®. Filie-se ao Intel® Developer Zone. Intel®Developer Zone

Comente também

2 Comentários

Rodrigo

Excelente artigo. Eu uso a tipificação fraca de uma forma híbrida, por exemplo: uma estrutura serializável que compõe o parâmetro de entrada e retorno do serviço, e dentro dela qualquer objeto serializável ou texto/xml é permitido. Assim é possível manter o uso do WSDL – o que facilita a referenciação do serviço no terminal – e ao mesmo tempo tirar vantagem do que você comentou.

Mas lembrando que a tipagem forte serve principalmente para que o terminal tenha certeza de que é compatível com o que o serviço faz, tem um grande porém nestas duas técnicas de tipagem fraca – se a equipe que mantém o serviço não tomar cuidado com compatibilidade reversa da estrutura, um terminal pode deixar de funcionar ou apresentar comportamento anormal por falta desta compatibilidade, e ser muito difícil e demorado entender o porque disso.

Já vi casos onde o terminal começa a executar ações anormais, e demora bastante tempo até alguém notar, e finalmente gerar uma OS para corrigir. Ou seja, embora a ideia é boa, ela gera outros problemas – “nem tudo são flores”.

Qual a sua opinião?