Oragon – Princípios de Design – Complexidade Reside na Arquitetura
Publicado em: domingo, 30 de jun de 2019

Alguns poucos lembram, pois alguns poucos estavam lá, quando comecei minha carreira profissional. Uma das coisas que me projetou rápido na Petrobras foi a capacidade de identificar padrões de reutilização e automatizar via abstrações e componentes. Ainda no ASP, em 2002, eu já havia criado alguns componentes, seja com VBScript para o ASP, ou com JavaScript para UI. Eu nunca me dei bem com trabalho repetitivo, principalmente pois esse tipo de trabalho me faz perder a atenção fácil. E o resultado é sempre o mesmo, faltou um detalhinho aqui, outro detalhinho acolá.

Antagonicamente, minha capacidade de concentração é meu forte desde aquela época, difícil mesmo era me concentrar fazendo CRUD’s!

Quando comecei a trabalhar com no Oragon, anos depois, eu percebi que precisava endereçar alguns assuntos de forma mais pragmática e rígida. Como rigidez e flexibilidade são tão antagônicos quanto e necessárias, levei um tempo até achar uma balanceamento que pudesse trazer o melhor dos 2 mundos.

Vale lembrar que estamos falando de LoB apps.

A propósito o que eu chamo de arquitetura é endereçado em diversas literaturas como infraestrutura, use esse termo se preferir, faz sentido atualmente.

E assim surgiu um dogma no Oragon: Complexidade deve residir na arquitetura.

Trocando em miúdos, você tem total liberdade para criar, extender, incrementar um mecanismo, ou uma solução “mirabolante”, mas precisa fazer seguindo algumas regras:

  • Encapsular a complexidade de acessar, configurar e usar frameworks, libraries e recursos que são objetivamente tecnológicos. O acesso a estes acontece via sua arquitetura.
  • Expor a API original deve ser uma opção a ser considerada e encorajada. Sua abstração deve oferecer comportamentos adicionais, mas não pode, sob hipótese alguma ser uma barreira para o uso da API original. A API original tende a oferecer recursos novos em um ciclo de vida diferente de sua abstração, portanto possibilitar o acesso a ela é importante.
  • Abstrações que sejam restritivas (eliminam o objeto abstraído do campo de visão do consumidor), devem ser desencorajadas, mas não são proibidas.
  • Tentar ao máximo reaproveitar abstrações existentes.
  • Tentar ao máximo refatorar e melhorar abstrações existentes para atender novos cenários.

Na prática temos 3 tipos de tarefas:

  • Nova Abstração / Reescrita do zero de uma abstração
  • Adição de cenários e funcionalidades a uma abstração antiga

Assim temos o seguinte conjunto de regras:

  • Se estamos diante de um problema novo, não resolvido pelas abstrações e mecanismos existentes, essa nova abstração ou mecanismo precisa:
    • Ser genérico.
    • Ser desacoplado das regras de seu negócio.
    • Precisa estar em um projeto que NÃO POSSUA, dependência (nem direta, nem indireta) com o código de negócio.
    • Precisa poder ser referenciada por teus projetos de negócio (e não importa se você chama de serviço, domínio, não importa qual arquitetura ou estratégia de modelagem seja a utilizada para o core do teu projeto).
    • É criado embarcada na solution do projeto, como uma abstração local.
    • Embora só atenda seu projeto, ela objetivamente está nascendo com o propósito de virar library, independente. Só não tem maturidade, na primeira implementação para ter seu uso disseminado indiscriminadamente.
  • Se estamos falando da substituição (reescrita) de um mecanismo, ou abstração, então temos outro conjunto de regras:
    • Seu desenvolvimento deve seguir as mesmas regras para uma abstração nova.
    • A nova abstração deve no mínimo atender a todos os requisitos da versão anterior (exceto quando requisitos/premissas estão errados).
    • É responsabilidade de quem está desenvolvendo a nova abstração, alterar 100% do código/configuração que usava a abstração anterior.
    • Não é possível conviver com 2 versões distintas que façam a mesma tarefa.

Assim desafios tecnológicos devem ser gerenciados por abstrações reaproveitáveis.

O que isso quer dizer?

Complexidade precisa ser gerenciada, ter um método de negócio que nele faça referência para diversas tecnologias diretamente aumenta a complexidade e dificulta a manutenção. O reaproveitamento e testes passam a ser dificultados e se tem ao longo do tempo uma colcha de retalhos, difícil de manter, difícil de gerenciar e com potencial para ser duplicada a qualquer momento. E isso nada tem a ver com monolitos ou microsserviços, estamos falando puramente de um código que faça uma tarefa.

Essas regras são desenhadas para inibir aventureiros e incentivar quem tem consciência de que pode fazer algo melhor. Por outro lado, empurra quem quer reinventar a roda na direção de refatorar um mecanismo, pois o esforço tende a ser menor.

Liberdade com responsabilidade

Você é livre para usar as tecnologias que bem entender e que forem necessárias. No entanto para fazer isso, você não faz de qualquer jeito, nem em qualquer lugar, nem deixa lixo pelo caminho. Seu código de negócio precisa continuar clean e sua arquitetura precisa continuar sendo coesa.

Esse conjunto de regras força na direção da abstração e do desacoplamento. Mas também te empurra para desenhar soluções de fato reaproveitáveis.

Seu projeto não é um playground

Embora toda abstração seja uma simplificação, toda abstração ganha um propósito adicional de ser reaproveitável. É preciso inibir síndromes como NIH (Not invented here). Esse modelo desencoraja o uso irracional de uma tecnologia da moda. E fica evidente por regras e gestão de histórico que algumas coisas podem ou não pode ser realizadas.

Genérico vs Específico, BDUF vs EDUF vs NDUF

Precisamos pontuar o que são esses acrônimos:

NDUF – No Design Up Front

EDUF – Enough Design Up Front

BDUF – Big Design Up Front

Em nome do EDUF, evitando BDUF, acaba-se realizando o NDUF.

Esse é um ponto polêmico: O que é Big? O que é Enough? Qual é o limiar entre eles? Muita gente confunde especificidade com acoplamento na hora de usar esses conceitos para construir seja lá o que for.

Sob a armadilha de achar que qualquer mínimo design é BDUF, se pratica NDUF indiscriminadamente. Da mesma forma que se confunde especificidade com acoplamento:

Especificidade tem a ver com a resolução objetiva de uma questão, já acoplamento tem a ver com o nível de interdependência.

Devemos nos atentar para a possibilidade de atender outras possibilidades, mas apenas para o design. Isso quer dizer que está tudo bem se você só implementou um cenário de uso, desde que tenha vislumbrado novos cenários e deixados pontos para abstrações futuras. Aplicar SOLID faz com que você, automaticamente, cumpra esse papel.

Minha dica é que o mínimo de design deva ser o uso de SOLID. Acredito que o design ideal de mecanismos arquiteturais, deva levar em conta a possibilidade desse mecanismo ser usado por outros. Pra isso é necessário pensar em cada componente ou classe como um elemento singular, fora do contexto de negócio, com um propósito próprio, com uma responsabilidade própria e assim entender como esse mecanismo pode atender outros consumidores futuramente. Quais os pontos de abstração são necessários? Como servir de base para novas implementações? Onde ser opinativo e onde não ser?

Aqui acredito que caiba um exemplo: Tenho para apreciação a interface IConfigurationResolver. A simplicidade dessa interface é o ponto central para novas implementações futuras. Hoje só temos a implementação de StaticConfigurationResolver, no entanto abre portas para implementações usando Secrets do Docker ou Vault’s dos mais variados vendors ou implementações como Consul ou Zookeeper ou Etcd. O poder de soluções simples mas genéricas é a possibilidade de compor implementações, reaproveitar para os mais variados fins. No github é possível ver onde essa interface é usada inclusive uma implementação feita com base em arquivos de configuração do NHibernate.

Não devemos inventar rodas quadradas

Ao criar um mecanismo é necessário endereçar problemas de forma a garantir que não esteja reinventando a roda, principalmente se essa roda for quadrada, ou seja: pior ou incompleta em relação ao que já existe. Novamente Not Invented Here é um problema que queremos endereçar aqui.

Aguçando o senso de propósito e suprimindo o ímpeto revolucionário

Esse sem sombra de dúvidas é o elemento mais controverso e polêmico, mas o que melhor expressa e separa crianças de adultos.

Muitas das soluções que encontramos em clientes não passam de PoC’s que foram para produção, ou testes que viraram implementações definitivas. A falta de gestão de código tem imenso potencial destrutivo. Há casos em que tecnologias são utilizadas sem o menor cabimento ou necessidade, ou uso equivocado de tecnologias empenhadas no papel errado, há casos em que o roadmap pessoal de estudos do desenvolvedor lidera a implementação de mecanismos e abstrações duplicadas ou desnecessárias, ou até a adoção de tecnologias que não fazem sentido naquele contexto.

Essa abordagem que proponho, principalmente no que diz respeito à reimplementação, prevê que o “legado” seja revisitado e refatorado para usar a nova solução, força 2 coisas relevantes:

  • Compreensão dos requisitos atendidos pela versão anterior do mecanismo/componente/abstração.
  • Compreensão das features da versão anterior do mecanismo/componente/abstração.

Em casos de reconstrução (reescrever uma abstração/mecanismo), espera-se que a nova versão substitua completamente a versão anterior, eliminando inclusive o código da versão anterior. Para isso todos os consumidores precisam ser migrados deixando de consumir a versão anterior para consumir a nova versão.

Caso a versão anterior continue sendo necessária, é sinal de que a nova implementação ainda não está acabada e a tarefa não está concluída.

Pois substituir a implementação anterior é um dos requisitos da tarefa.

Na medida que compreendemos melhor e conhecemos o esforço de criar algo novo, ponderamos sobre esse esforço de criação de algo novo, em comparação à customização da versão anterior. Afinal, é possível e talvez até provável que com pouco redesign se alcance o objetivo com a implementação anterior. E é esse um tipo de refactoring que precisa ser encorajado.

Quem está convicto de que tem condições de fazer melhor o faz por puro e simples senso de propósito. Pratique o desapego, não importa quem tenha feito a melhor versão, o que importa é que temos uma versão melhor. Esse tipo de refactoring precisa ser encorajado.

A construção de um ativo de arquitetura

Essa relação com seus mecanismos é uma relação sempre positiva: Ou você está criando algo novo ou está melhorando/adaptando o que estava pronto para atender a novos cenários. Se estiver reimplementando algo, sua substituição tende a ser no mínimo melhor. O resultado dessas decisões é a produção de um ativo de arquitetura, algo com potencial para crescer e possivelmente se tornar uma solução compartilhada entre projetos.

Pensando grande, começando pequeno

Mesmo vislumbrando que essa solução, para atender a um problema comum, poderá ser promovida a library compartilhada entre projetos, seu desenvolvimento acontece no âmbito local, começa dentro da solution e repositório git do projeto que primeiro demandou aquilo, resolvendo um problema por vez, começando pequeno. Com o tempo e maturidade nesse design ou após diversas intervenções, e enfim alcançando a maturidade e estabilidade, é possível cogitar sua promoção para uso geral.

A promoção de uma implementação local para uma implementação global segue outro conjunto de regras que precisam ser discutidos em algum momento.

Benefícios

Afinal, quais são os benefícios dessa abordagem?

  • Redução monumental do acoplamento.
  • Centralização lógica das abstrações (Abstrações estão na mesma camada).
  • Descentralização física das abstrações (Abstrações estão em projetos independentes e reaproveitáveis).
    • Começam no mesmo projeto
    • Quando promovidas, ganham status de projeto
  • Independência e capacidade de reaproveitamento de abstrações
  • Maior coesão
  • Gestão de código mais eficiente, consequentemente alterações são mais efetivas, embora mais complexas.
  • Redução de complexidade acidental causada pela adição não estruturada e planejada de tecnologias.
  • Facilidade de gestão de

Conclusão

Esse tipo de abordagem evita que se crie abstrações específicas demais para o negócio, com baixo reaproveitamento, e nenhuma coesão.

Algo que vou falar mais detalhadamente quando abordar Abstrações, é que elas são promovidas de abstrações do projeto para abstrações globais, e viram projetos autônomos na medida que ganham maturidade.

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!

 

Eventos passados

outubro 2020
setembro 2020
agosto 2020
Nenhum evento encontrado!
Carregar Mais

Publicidade

Assine

0 comentários

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
22 outubro 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

setembro

outubro 2020

novembro
DOM
SEG
TER
QUA
QUI
SEX
SAB
27
28
29
30
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
6
7
Events for 30th setembro
Events for 1st outubro
Events for 2nd outubro
Sem Eventos
Events for 3rd outubro
Sem Eventos
Events for 4th outubro
Sem Eventos
Events for 5th outubro
Sem Eventos
Events for 6th outubro
Sem Eventos
Events for 7th outubro
Sem Eventos
Events for 8th outubro
Sem Eventos
Events for 9th outubro
Sem Eventos
Events for 10th outubro
Sem Eventos
Events for 11th outubro
Sem Eventos
Events for 12th outubro
Sem Eventos
Events for 13th outubro
Sem Eventos
Events for 14th outubro
Sem Eventos
Events for 15th outubro
Sem Eventos
Events for 16th outubro
Sem Eventos
Events for 17th outubro
Sem Eventos
Events for 18th outubro
Sem Eventos
Events for 19th outubro
Sem Eventos
Events for 20th outubro
Sem Eventos
Events for 21st outubro
Sem Eventos
Events for 22nd outubro
Events for 23rd outubro
Sem Eventos
Events for 24th outubro
Sem Eventos
Events for 25th outubro
Sem Eventos
Events for 26th outubro
Sem Eventos
Events for 27th outubro
Sem Eventos
Events for 28th outubro
Sem Eventos
Events for 29th outubro
Sem Eventos
Events for 30th outubro
Sem Eventos
Events for 31st outubro
Sem Eventos
Share This