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 seguindo dois ou mais padrões antagônicos (sim, na mesma base), diversos padrões nomenclatura. As bases não possuíam um responsável. Eram “da empresa”, não havia um dono.  O acoplamento do modelo de dados era entre database/tecnologia. Isso quer dizer que um dado em uma base SQL Server só deixa de ser dado vira informação em conjunto com dados que estavam em outras bases MySQL, por exemplo.
  • Alto acoplamento entre as diversas aplicações e suas bases de dados.
  • Um parque de aplicações baseadas em .net framework 2.0, usando ADO.NET puro para escrever o acesso a dados: Manualmente. Uma quantidade infinita de código de acesso a dados 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. Muitas delas exigiam rodavam na IDE, em modo debug pois um dev trocava algumas strings e executava em modo supervisionado. Isso não era ao acaso, era a forma como processávamos dados de produção no dia-a-dia.
  • Gerência de configuração inexistente, havia um SVN, mas mal era usado para versionador de código.
  • Nenhuma gestão de releases.
  • Centenas de micro-aplicações responsáveis por micro-tarefas, sem documentação, sem 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.
  • Todo o conhecimento tecnológico dependia exclusivamente da memória do time ou daqueles que passaram pela equipe e ainda estavam na empresa.

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

Primeiro passo foi definir um fluxo geral de gerência de configuração: A partir de então 100% do que fazíamos era 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 tirassem o sono, poderíamos partir para a identificação e melhoria da vida das pessoas.

  1. Identificação do cenário atual

Um dos processos, que considero dos mais importantes da empresa é a ingestão de conteúdo, por ele recebíamos milhares de XML’s e mídias(WAV, AVI) todos os dias, são diversos formatos e padrões de integração, o que nos demandava 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 Moleculá (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 possibilidade de 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 só 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á haviam empenhado algum esforço nessa mesma iniciativa no passado, mas ignorei. Acreditei que com alguma supervisão mais técnica seria possível: Não. não era! Subestimei o padrão “dos infernos”. Era surreal, se modelássemos, seriam pelo menos 300 tabelas para gerir. Era necessário encontrar uma alternativa.

Após a desistência de bases SQL, pensei em usar uma base XML, mas nada me agradou: nem bancos, nem tecnologias, nada. 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 um sucesso, extremamente eficiente, agora permitindo consultas nos dados que eram do XML, e agora são 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, então tenho o melhor dos mundos!

3) A falta de visibilidade dos pacotes (pacotes são entregas de 1 XML de metadados com suas N mídias) e a pulverização do FileSystem era problema do passado e do presente, agravante e complicómetro para o processo. Analisar diretórios de servidores de produção era tarefa diária. Entenda, eram pelo menos 16 storages, onde cada pacote poderia estar em qualquer um deles. O processo de gestão de espaço em disco era apontar o FTP para O MESMO path relativo EM OUTRO DISCO. Todos os 16 storages possuíam as mesmas estruturas base, e o endpoint do FTP hora apontava para qualquer um desses 16 storages, de acordo com a disponibilidade de discos.

Gerir esse caos, era complicado, pois o acesso a disco é muito custoso. Nesses storages onde petabytes eram armazenados em poucas pastas, era surreal fazer qualquer busca ou leitura. Não havia eficiência na leitura desses discos, portanto, eu precisava de alguma representação desse file system, mais rápida e eficiente. Algo como um file system virtual que representasse o file system físico. Pois diversas análises precisam ser feitas com base na estrutura, nos nomes do arquivos, para que pudesse tomar uma decisão sistêmica. Na prática eu precisava consolidar os pacotes fragmentados, para que no final do processo, fosse eliminada a fragmentação.

Criei uma base SQL para gerir o processo com uma visão semelhante à do FileSystem (Diretórios, arquivos, tamanho), tudo exceto o conteúdo do arquivo em si, permitindo com queries determinar exatamente o que cada disco possui. Agora não precisamos estar em contato direto com o File System, queries em um banco SQL mostravam todo a estrutura do File System. Um processo simples fazia a sincronização periodicamente. Uma característica legal é que por mais que o pessoal de infraestrutura cuidasse do FTP da mesma forma como sempre fizeram, meu trabalho poderia ser feito de forma coesa, reduzindo os impactos dessas decisões. Por outro lado, para o processamento das tarefas, havia uma escolha de prioridade entre os discos, isso permitia que eu mesmo não sofresse com as necessidades específicas de gerir storages tão grandes.

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-side, com múltiplos databases, hora em contexto transacional, hora somente 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, gera uma quebra no build: automaticamente, aumentando a segurança e resiliência na 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 é mais), não havia 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. Outro ganho direto é a resolução de connection leaks.

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. A sincronização do File System com o banco SQL que citei acima, também era agendada aqui.

Mensageria e Exception Handling

Para escalar a solução, um pipeline foi criado em cima do RabbitMQ para processar todo o fluxo de trabalho das 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. Mais tarde, após inúmeros problemas, substituímos o Sentry por ElasticSearch.

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!

 

[03/04/2018] – Revisão do Texto para melhorar a explicação sobre algumas necessidades.

Luiz Carlos Faria

Mensagem do Autor

Espero que goste desse post. Não deixe de comentar e falar o que achou. 

Se acha que esse post pode ajudar alguém que você conheça, compartilhe!

 

Próximos Eventos

Nenhum evento encontrado!

Publicidade

Assine

5 Comentários

  1. Ricardo

    Olá Luiz! Tudo bem?

    Lendo o texto fiquei com uma duvida, você cita no texto “…Equipe tecnicamente desatualizada, que vivia uma era inteira apagando incêndios…” e “…GAP técnico…”.
    Neste cenário que aqui expôs, foi necessário alterar o time técnico em questão? Digo, você teve que treinar o pessoal ou ouve a dança das cadeiras com relação aos cargos?

    Obrigado!

    Responder
    • Luiz Carlos Faria

      Ótima Pergunta Ricardo. Existem 2 tipos de profissionais, os que jogam o jogo e os que burlam as regras. Quem joga o jogo, entende o que está mudando e ok, segue o jogo, tentam fazer, não jogam contra. Mas quem burla as regras tende a responder para clientes externos da seguinte forma: “Ahh, se eu fizer sem isso, sem aquilo, sem aquilo outro, eu faço mais rápido”. Enfim, quer uma confirmação para fazer algo da forma não combinada. Sem arquitetura, provavelmente sem testes, enfim.

      Embora eu não tenha demitido, esse segundo perfil eu acredito que não agregue, que seja mais nocivo do que benéfico, hoje eu teria demitido.

      Mas não o fiz. Eu simplesmente após tomar alguns bypasses desses eu simplesmente me envolvi diretamente ao ponto de não deixar o fluxo seguir sem uma avaliação da forma e como as coisas estavam sendo feitas. Se olhar a imagem, vai perceber que parte do processo é tirar autonomia do dev de colocar código em produção. E eu defendia com minha vida (na empresa, claro) que código fora do combinado não chegaria mais em produção, para evitar toda essa ingerência sobre o código técnico. Deu certo na maioria das vezes, mas não todas. Ok!

      Para ajudar, eu precisava contratar gente que estivesse mais próxima e mais a par da arquitetura que eu estava propondo, eu precisava de apoiadores, e para isso precisava trazer pra dentro quem conhecesse algumas tecnologias-chave, principalmente injeção de dependência, dependency injection. E não bastava conhecer mais ou menos, tinha de ter um certo nível de profundidade. Pode parecer bobo, mas a gente naquela época ia muito além de um simples register/resolver.

      Assim eu cuidei do processo de contratação de novos membros, e escolhi com cuidado perguntas que faria. Meu medo era contratar hater em vez de aliados, então nesse processo eu fiz questão de descartar quem era hater de algumas tecnologias específicas (NHibernate e Spring.NET) e principalmente quem nunca havia trabalhado com .NET + Open Source (bancos SQL, bancos NoSQL, Filas, etc). Essa galera que eu estava trazendo e trouxe 4 novos pelo menos, precisavam ter uma cabeça mais aberta. Eles me ajudariam a evangelizar os demais.

      Esse foi o combo, foi assim que com uma galera nova, motivada, conseguimos produzir comportamentos bons nos demais. Embora se tenha tido muita rejeição no início, consegui abstrair a complexidade de diversas tecnologias, assim viabilizando que mesmo quem não conhecia nada de RabbitMQ, pudesse trabalhar com, de forma transparente. Sem mudar ou implementar código específico, apenas algumas regras eram necessárias para se ter um consumidor apontando para um método, com toda a segurança necessária para esse tipo de processamento.

      Como estratégia para evitar aventuras, a só tinha de fato complexidade, na arquitetura, hoje é mais comum ser chamado de infraestrutura, mas eu ainda chamo esse tipo de projeto de projeto de arquitetura. Quem queria “inventar moda” tinha de fazer de forma genérica para que todos pudessem aproveitar a nova solução, não importa o âmbito.
      Fazia parte desse escopo trocar as implementações antigas (pela nova, recém criada), portanto, você teria de lidar com códigos que nunca viu antes. Só quem estava convicto do potencial de entregar algo melhor, tinha motivação para fazer a coisa certa. Quem queriam apenas fazer uma gambiarrazinha, fazer um testezinho com uma tecnologia, que acabaria chegando em produção, era automaticamente desencorajado.

      Essa abordagem evita muita gambiarra e gente reinventando uma roda quadrada. Eu nunca desencorajei a fazer no lugar central, na arquitetura, o requisito que eu adicionava a qualquer implementação nessa área era que não fosse dependente do negócio, precisava ser genérica, reaproveitável, e se fizesse a mesma coisa que uma versão anterior, deveria substituir a anterior. Fato que isso gerou soluções ótimas, principalmente uma de consumo de filas, que hoje me parece complexa demais, mas atende e continua atendendo, quase 6 anos depois.

      Responder
      • Ricardo Augusto Lamb

        Luiz,
        Muito obrigado pela resposta!
        Esclareceu muito, e ao mesmo tempo gerou mais dúvidas! rsrsrs
        Penso que neste processo todo, lidar com arquitetura, com códigos, com tecnologia como um todo e por fim este tipo de situação no time, você deve ter tido uma “gordura” de tempo muito boa para manobrar tudo isso! Ou estou errado, tudo isso aconteceu perfeitamente dentro do escopo de tempo?? (emoji de carinha pensando) 🙂

        Obrigado!!
        Abraços!

        Responder
        • Luiz Carlos Faria

          Quem controla o tempo e quem dá prazo é o time de tecnologia. Não é uma questão de ter gordura. Pra mim aquilo era necessário, eu preciso lidar com o problema de forma profissional e portanto, embutir esse custos nos projetos do dia-a-dia faz parte. Senão a conta chega um dia.

          Ninguém vai ligar se vc entregou mais rápido mas deixou um débito ou manteve outro débito.

          Quando você dá o prazo para uma atividade, você tem de dar o prazo para fazer tudo a respeito dela.
          Ceder às pressões do negócio, é, me perdoe o termo, burrice.
          Pois isso vai se voltar contra você mesmo.

          Senso de urgência é uma coisa que tentam repassar e propagar da presidência até o mais raso executor, no entanto se você trabalha sério, você consegue espaço para organizar isso. Se tudo é prioritário, então nada é prioridade. E esse era meu lema. Se tenho 10 atividades como mesmo nível de urgência e criticidade, eu posso escolher entre qualquer uma, é meu direito. Como gestor técnico e organizacional daquele time, eu tinha essa autonomia.

          Vamos supor, que pela perspectiva do cliente seja adicionar um campo em uma tela, no entanto, entendendo o problema, na verdade deva ser criada uma nova tabela, em vez de adicionar uma coluna em outra. Criar uma coluna tem algum impacto, mesmo que pequeno. Mas é infinitamente menor do que o impacto de criar uma tabela, ou quebrar uma tabela em 2. Se para atender à demanda você adicionar um mero campo, só pra atender a expectativa de prazo do teu cliente, você está cometendo um erro grave, você vai pagar a conta disso em algum momento ou ainda, sairá da empresa e outro pagará a conta por você. Mas nesse caso não há outro culpado, senão você!

          Meu papel ali era transformar aquela equipe, aquela área.
          E conduzi com sucesso no primeiro ano. No segundo ano, era hora de jogar poeira para baixo do tapete, e não sabia disso. Não estava claro para mim. Eu só fui podado.

          Mas ainda assim, executei ali um trabalho muito consistente, principalmente no que diz respeito à mindset. Fazer a coisa certa em vez de fazer a coisa mais conveniente. Consegui embutir e engordar prazos para atender à demandas de organização dos projetos, pena que não tive tempo para finalizar minha obra.

          Eu não conseguia mais não progredir. Minha ambição técnica era muito maior do que o espaço que eu tinha para trabalhar e um belo momento não deu mais para sustentar essa relação.

          Responder
          • Ricardo

            Luiz,

            Ótimos esclarecimentos!
            E parabéns pelo ótimo artigo!! Muito rico em detalhes, isso nos dá uma real noção de qual é o papel correto a ser desempenhado nestes momentos!
            Muito obrigado por compartilhar!!

            Abraços!

            Responder

Enviar um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Próximos Eventos Próximo
11 agosto 2020
  • 00

    dias

  • 00

    horas

  • 00

    minutos

  • 00

    segundos

Lives

Fique de olho nas lives

Fique de olho nas lives no meu canal do Youtube, no Canal .NET e nos Grupos do Facebook e Instagram.

Aceleradores

Existem diversas formas de viabilizar o suporte ao teu projeto. Seja com os treinamentos, consultoria, mentorias em grupo.

Agenda & Eventos

julho

agosto 2020

setembro
DOM
SEG
TER
QUA
QUI
SEX
SAB
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
Eventos para agosto

1st

Eventos para agosto

2nd

Eventos para agosto

3rd

Sem Eventos
Eventos para agosto

4th

Sem Eventos
Eventos para agosto

5th

Sem Eventos
Eventos para agosto

6th

Sem Eventos
Eventos para agosto

7th

Sem Eventos
Eventos para agosto

8th

Sem Eventos
Eventos para agosto

9th

Sem Eventos
Eventos para agosto

10th

Sem Eventos
Eventos para agosto

11th

Eventos para agosto

12th

Sem Eventos
Eventos para agosto

13th

Sem Eventos
Eventos para agosto

14th

Sem Eventos
Eventos para agosto

15th

Sem Eventos
Eventos para agosto

16th

Sem Eventos
Eventos para agosto

17th

Eventos para agosto

18th

Eventos para agosto

19th

Eventos para agosto

20th

Eventos para agosto

21st

Eventos para agosto

22nd

Sem Eventos
Eventos para agosto

23rd

Sem Eventos
Eventos para agosto

24th

Sem Eventos
Eventos para agosto

25th

Sem Eventos
Eventos para agosto

26th

Sem Eventos
Eventos para agosto

27th

Sem Eventos
Eventos para agosto

28th

Sem Eventos
Eventos para agosto

29th

Sem Eventos
Eventos para agosto

30th

Sem Eventos
Eventos para agosto

31st

Sem Eventos
Share This