Melhores PostsOragon ArchitecturePatterns & PracticesSolutions & Design Gallery

As vezes sou questionado sobre meus desenhos de arquitetura e porque criar tantas abstrações, tanta configuração e tantas dependências e frameworks de terceiros, alguns que só eu e um tibetano conhecemos. No post Como definir Arquitetura de Software, cito quais são os pensamentos primários necessários para se desenhar um arquitetura, mas vamos aplicar isso a um contexto real para exemplificar.

Tudo começa com minha chegada na atual empresa, onde encontrei um cenário extremamente complexo e difícil de se trabalhar.

  • Umas 8 bases de dados, entre SQL Server e MySQL, cada uma com diversos padrões diferentes de nomenclatura. Sem owner definido e com alto acoplamento entre elas e entre as diversas aplicações.
  • Um parque de aplicações baseadas em framework 2.0, usando ADO.NET para escrever o acesso a dados manualmente. Uma quantidade infinita de código realizando N+1 Anti-Pattern por causa da forma como o acesso a dados era escrito.
  • Dezenas de micro aplicações, forks de uma aplicação inicial que realizava um processo batch de importação de conteúdo extremamente importantes para o business.
  • Gerência de configuração inexistente, usando SVN apenas como versionador de código
  • Centenas de micro-aplicações responsáveis por micro-tarefas, sem documentação, identificação e algumas sem sequer os fontes. Sem falar que muitos dos fontes sequer eram versionados ou estavam defasados no repositório de código.

Dá para ver que há muito a se trabalhar nesse lugar. Ok, assim começamos.

Primeiro passo foi definir um fluxo geral de gerência de configuração. 100% do que fazemos é versionado. 100% do que conhecemos também precisa ser. Agora com algum alívio, por não ter mais medo de que HD’s queimados me tirem o sono, podemos partir para a identificação e melhoria da vida das pessoas.

  1. Identificação do cenário atual

Um dos processos, que considero um dos mais importantes da empresa é a ingestão de conteúdo, por ele recebemos milhares de XML’s e mídias todos os dias, são diversos formatos e padrões de integração, e nos demanda uma quantidade razoável de storage, alguns petabytes. Esse processo era composto por diversas micro-aplicações, onde cada uma era um fork de um projeto base, e nela tínhamos um desenvolvedor gerindo esse código, uma loucura para uma pessoa só. Óbvio que daria merda em algum momento. Fiz o trabalho de entender o cenário de negócio e compreender quais são os gaps do processo atual. Entre eles estão:

  • Alta segmentação das versões da base de código, cada uma com ligeiras mudanças para atender ligeiras diferenças entre parceiros.
  • Alta complexidade em cada uma das aplicações que possuíam formatos de XML diferentes.
  • Baixa completude no processamento do conteúdo recebido (boa parte das aplicações, ou precisava de tapas na hora de processar, ou só processava parte do XML que recebíamos)
  • Alta complexidade na gestão do processo
  • Falta de visibilidade do estágio do processo
  • Nenhuma escalabilidade
  • Paralelismo Molecolá (o moleco lá tentava paralelizar com braço esquerdo e direito)
  • Equipe tecnicamente desatualizada, que vivia uma era inteira apagando incêndios
  • Equipe reduzidíssima

Como resolver essa questão:

1) Durante as discussões sobre os formatos de XML, vimos que muitos parceiros, e o mercado em si, estavam convergindo para um padrão chamado DDEX. Esse standard serve para qualquer integração imaginável (programas, músicas, vídeos, ringtones, wallpapers etc). Apostar nesse formato foi uma escolha ousada, mas trouxe muito resultado. Como o padrão é extremamente amplo e aberto para diversas finalidades, há campos demais para dados de menos. O resultado é a interpretação particular de cada parceiro sobre cada uma das áreas do XML. São 8 formas diferentes de se tirar um conteúdo do ar, de forma implícita ou explícita entre outras variações bem peculiares. Mesmo com essa complexidade, unificar o modelo de entrega nesse formato nos trouxe a segurança para criar uma única aplicação para processar esse conteúdo, então conseguimos ter uma visão unificada do processo, e ainda escalável.

2) Viabilizar a análise eficiente dos XML`s

Primeiro pensei em um database SQL para armazenar o conteúdo do XML, mas quando estávamos próximos de modelar 100 tabelas, e não estávamos sequer na metade do mapeamento, pedi que parassem a atividade para que pudesse repensar. Já houvera no passado a mesma iniciativa, mas era surreal, se modelássemos, seriam pelo menos 300 tabelas para gerir. Após a desistência de bases SQL, pensei em usar uma base XML, mas nada me agradou. Então pensando muito na possibilidade, optei por usar o MongoDB para realizar essa tarefa. Como MongoDB não trabalha com XML, geramos conjunto de classes com base no XSD, fazendo o parser direto de XML para .Net, que em seguida é persistido diretamente os objetos no MongoDB. O resultado foi extremamente eficiente, agora permitindo consultas nos dados do XML (BSON) direto no MongoDB. Foi uma excelente decisão, poupando muito tempo e facilitando muito o trabalho. Pelo fato das classes terem sido geradas com base no XSD, as garantias de formato estão todas presentes.

3) A falta de visibilidade dos pacotes e a pulverização do FileSystem eram problemas graves para o processo. Analisar diretórios de servidores de produção era tarefa diária. Ainda é enquanto não estabilizarmos a solução.

Uma base SQL foi criada para gerir o processo com visão semelhante à do FileSystem, permitindo com queries determinar exatamente o que cada disco possui. Agora não precisamos estar em contato com o File System, bastaria operar uma solução Web para gerenciar o fluxo. Isso se a solução web tivesse sido construída. Devo iniciar essa tarefa em alguns dias.

4) Criação de uma infraestrutura de arquitetura robusta que facilitasse e promovesse o refactoring contínuo

Geração de Código

O Oragon Architecture foi fundamental para a criação desse modelo. Com ele geramos código de acesso a dados, para todas as bases necessárias, gerando código e configuração para NHibernate e FluentNHibernate. Trabalhando com MySQL e SQL Server Side-By-Syde, com múltiplos databases, hora transacional, hora read-only. Agora, a ausência de um owner de cada database, não representa mais um problema. O banco muda, mas periodicamente geramos código a partir do banco, e é comum ver coisas mudando. O que nos afetar, diretamente e for incompatível, quebra o build automaticamente, aumentando a segurança de consistência da solução. Da mesma forma que geramos código com uma grande riqueza de detalhes, estamos aptos a regerar os bancos a qualquer momento, e identificar o que mudou estruturalmente no banco, bastando analisar nosso mapping com a ajuda de históricos do scm.

AOP

Sob o framework de AOP do Spring.Net, o Oragon Architecture implementa uma série de aspectos que torna viável trabalhar com Redis, MongoDB, MySQL, DB2 e SQL Server de forma transparente e simples. Um único template é necessário para todos os bancos SQL citados. Para o NoSQL, como é schemaless, não há como gerar código, mas os aspectos são úteis por definir a conectividade da mesma forma para todas as soluções. Do ponto de vista do código, nenhum OpenSession ou Connect é visível, apenas precisa-se marcar os métodos com os atributos corretos para que a infraestrutura cuide de todo o resto.

Ainda falando do Spring.Net, jobs agendados foram criados com Quartz.Net para iniciar operações de negócio agendadas, como emissão de relatórios que anteriormente eram criados manualmente periodicamente entre outras operações recorrentes agendadas.

Mensageria e Exception Handling

Para escalar a solução, um pipeline foi criado em cima do RabbitMQ para processar todo o fluxo de trabalho necessário para as importações, desde a gestão (monitoramento) do file system, até a ingestão propriamente dita. Nesse modelo o Oragon fornece as abstrações para que você só implemente as operações necessárias em cada passo do pipeline, sem que seu código saiba sequer que faz parte de um. Assim basta programar uma operação de negócio e plugar no pipeline, caso haja erro no processamento, lance suas exceptions e o próprio orquestrador (Oragon) se encarregará de colocar a mensagem na fila de erro, para que a mensagem não seja reprocessada infinitamente, com o mesmo status de erro. Geralmente os erros são de duas naturezas, ou o cliente errou no contrato ou o código encontrou uma situação não prevista, há um terceiro cenário, pouco comum que é uma falha em validação de negócio. Para essa temos um fluxo de tratamento diferenciado.

Em todos os casos, as exceptions são logadas, automaticamente, pelos aspectos Oragon Architecture, em filas RabbitMQ, isso com ajuda do pacote Spring.NET AMQP, posteriormente são armazenadas no Sentry sem causar gargalos na aplicação.

Worker Process

Agora, estamos aptos a iniciar diversos hosts de processos, para consumir uma infinidade de filas, segmentando cada operação por parceiro se assim nos interessar. Conseguimos trabalhar com diversos processos em diversas máquinas, escalando até quanto o hardware e os databases suportarem.

A arquitetura, cheia de abstrações viabilizou tomar as decisões que envolviam estratégia de deploy o mais tarde possível. Esse é o ganho trazido por uma aplicação extremamente configurável. Mas não há almoço grátis. As configurações são tão relevantes quanto o código e são extensas. No código, não precisamos ver sequer um try-catch, e não há nada de errado nisso. Os Aspectos são responsáveis por pela gestão e tratamento das mensagens. As operações transacionais, são geridas pelos aspectos e eles são responsáveis pelo rollback em caso de erro. As configurações são muito mutáveis e maleáveis, mas são de extrema importância para o fluxo. Enfim, toda escolha, uma renúncia.

Continous Integration e Continuous Deploy

Para o processo de build e deploy, havíamos configurado o Jenkins, mas agora estou apanhando um bocado do TeamCity. mas em breve teremos novamente um processo de build e deploy automatizado nele.

Esse foi o roadmap de um ano de trabalho. É realmente muito excitante trabalhar com essas tecnologias, mas também bem relevante conseguir atender e endereçar todas essas demandas.

Hoje a maior dificuldade está no Business. Compreender e estabilizar as expectativas quanto ao business, que no caso de um processo de importação de metadados, é preencher as devidas tabelas, garantindo retro-compatibilidade com um parque de aplicações complexo e infinito.

Resumo

Bom, novamente, os frameworks utilizados foram utilizados para sanar necessidades reais.

Spring.Net para IoC, DI e AOP

Oragon Architecture para AOP (implementação)

Quartz.Net para Agendamentos

TopShelf para Serviços Windows (joguei um monte de código fonte fora, depois que achei esse framework)

T4 para os templates de geração de código

Spring AMQP para abstrações com RabbitMQ

Lembre-se das premissas:

Equipe:

Equipe reduzida.

Contexto:

GAP técnico, falta de Gerência de Configuração, e segmentação do código. Falta de padronização de recebimento de conteúdo e dificuldade de gestão de código, plataforma e manutenção.

Enfim, Arquitetura

A correlação entre as diversas peças que irão compor a solução, cada uma oferecendo uma feature de negócio.

Espero que tenha gostado!

Comente, compartilhe, curta!

Gostou? Então aproveite para curtir, compartilhar e enviar comentários, dúvidas ou sugestões.

Conheça o Grupo Arquitetura de Softwate | .NET: Facebook e Telegram
Você pode se manter atualizado(a) pelos canais: Site, Youtube, Facebook, Twitter, Telegram, Linkedin e pode entrar em contato por TelegramEmail