O terceiro encontro do Clube do Livro da comunidade Leitura Dev abordou os capítulos 9 ao 13 do livro “Fundamentos da Arquitetura de Software” de Mark Richards. A facilitadora Jack Nascimento conduziu a sessão, reforçando que o encontro não é uma aula, mas um espaço de troca sobre a leitura. As orientações iniciais foram:
- Levantar a mão antes de falar para organizar a conversa.
- Manter o foco no tema dos capítulos discutidos.
- Dar espaço para todos participarem, respeitando o tempo finito da sessão.
- Não existe resposta certa: o que enriquece a discussão são as perspectivas e experiências de cada um.
- A agenda foi planejada para terminar até às 21h30 ou 21h40.
- Jack Nascimento preparou slides com curadoria dos pontos mais relevantes dos capítulos 9 ao 13, deixando espaço para adições dos participantes.
- Havia participantes novos no encontro, que foram bem-vindos.
- O livro não define claramente o que é padrão arquitetural, apenas estilo arquitetural. (Wesley)
- Estilo arquitetural é mais amplo; padrão arquitetural resolve problemas específicos. (Marcel)
- Exemplos de padrões arquiteturais: arquitetura hexagonal, Onion Architecture, CQRS.
- Exemplos de estilos arquiteturais: microsserviços, monolito modular, pipeline, microkernel.
- Confusão comum: alguns nomes são compartilhados entre estilos e padrões, como “em camadas” e MVC.
- O livro não diferencia os dois conceitos em vários momentos, o que gera confusão. (Wesley)
- Faltam disclaimers no livro deixando claro quando o autor fala de estilo versus padrão. (Wesley)
- Microsserviço e monolito modular são estilos, não padrões, pois são mais amplos:
- É possível implementar microsserviços com MVC, com Big Ball of Mud, com ports and adapters.
- Arquitetura hexagonal não é abordada explicitamente no livro; o capítulo 20 trata de padrões arquiteturais com foco em CQRS, separação de acoplamento de domínio e broker domain. (Marcel)
- Fornece uma maneira concisa e clara de descrever conjuntos de características de uma arquitetura. (Marcel)
- Abrange quatro dimensões principais:
- Topologia de componentes: organização lógica (camadas, domínio etc.).
- Arquitetura física: implantação monolítica ou distribuída.
- Forma de comunicação: chamada de método, API REST, mensageria, RPC.
- Topologia de dados: dados segregados por módulo ou compartilhados de forma monolítica.
- Novos estilos emergem de ecossistemas em constante evolução. (Marcel)
- Microsserviços surgiram para resolver problemas de escalabilidade.
- Foram possíveis graças à evolução de sistemas operacionais e ao DDD.
- É o estilo arquitetural que surge quando não há estilo definido explicitamente. (Jack Nascimento)
- Características principais:
- Crescimento incontrolável.
- Reparos repetidos e improvisados.
- Informações compartilhadas de forma descontrolada entre componentes.
- Toda informação tende a se tornar global ou duplicada.
- Estrutura geral mal definida.
- Gera problemas de implantação, testabilidade, escalabilidade e desempenho. (Marcel)
- É resultado da falta de governança arquitetural. (Marcel)
- Remete a sistemas reais que muitos participantes já encontraram na carreira. (Marcel, Jack Nascimento)
- Na era da IA, a degradação arquitetural acontece de forma muito mais rápida:
- A IA replica o que está no código e nas guidelines do projeto.
- Sem guardrails e direcionamento arquitetural, a IA faz o que qualquer dev desinformado faria. (Marcel)
- Pequenas degradações acumuladas se tornam Big Ball of Mud mais rapidamente. (Wesley)
- O problema não é a IA em si, mas a falta de cuidado humano na revisão do que ela gera. (participante)
- Todo software tende ao caos com o tempo; o papel do dev é retardar essa degradação. (Renato)
- A pressão por entrega é um fator real que contribui para a degradação, mas não justifica abandonar boas práticas permanentemente. (participante)
- O capítulo sobre conformidade arquitetural do próprio livro já responde como evitar essa degradação: assegurar conformidade com decisões de arquitetura e princípios de design. (Jack Nascimento)
- Aborda como organizar os componentes internamente dentro de uma arquitetura. (Marcel)
- Dois tipos principais:
- Separa componentes de alto nível por domínio ou fluxo de trabalho (checkout, pedidos, busca).
- O código “grita” o modelo de negócio, não a tecnologia utilizada. (Uncle Bob, citado por Marcel)
- Inspirado no Domain-Driven Design (DDD).
- Base do estilo arquitetural de microsserviços.
- Vantagens:
- Mais próximo de como o negócio funciona.
- Facilita equipes multifuncionais por módulo.
- Facilita migração para arquitetura distribuída.
- Desvantagem: código de personalização pode aparecer em múltiplos locais.
- Não está necessariamente ligado a microsserviços; é possível ter monolito particionado por domínio. (Arthur)
- Separa em camadas técnicas: apresentação, regras de negócio, serviço, persistência.
- Padrão MVC é um exemplo clássico.
- Vantagens:
- Fácil de entender.
- Separação clara de camadas.
- Alinhado com arquitetura em camadas.
- Desvantagens:
- Pode deixar o domínio desorganizado.
- Grau mais alto de acoplamento global.
- Dificuldade para migrar para sistemas distribuídos.
- É possível combinar os dois: particionamento por domínio no nível macro e técnico internamente em cada módulo. (Arthur)
- Diferença fundamental entre arquitetura monolítica e distribuída:
- Monolítica: unidade única, chamadas locais no mesmo processo.
- Distribuída: múltiplas unidades de implantação, comunicação via rede.
- As falácias são premissas falsas que as pessoas assumem como verdade ao adotar arquitetura distribuída.
- Redes são projetadas para ser tolerantes a falhas, mas falhas acontecem.
- Mecanismos como timeout existem para lidar com isso.
- Chamada local: escala de microssegundos.
- Chamada remota via HTTP, TCP, REST, RPC: escala de milissegundos.
- Diferença de ordem de grandeza: mil a cem mil vezes maior.
- Latência de cauda longa prejudica o desempenho de sistemas distribuídos.
- Conceito de P99 (percentil 99): ferramenta para monitorar se a latência está dentro do aceitável. (Wesley)
- Exemplo: se 99% das requisições estão abaixo de 100ms, está dentro do aceitável.
- Quando ultrapassa, é hora de rever arquitetura e decisões de design.
- Exemplo real: requisição que deveria demorar 100ms passou a demorar 500ms após adição de microsserviços desnecessários. (Wesley)
- Muitas requisições por segundo com payload grande geram o problema de “stamp coupling”:
- Serviço envia e recebe estruturas de dados muito grandes.
- Aumenta consumo de rede e degrada performance.
- Solução: contratos que informam apenas o pedaço da informação necessário.
- Não se deve assumir que a rede é segura.
- Sistemas distribuídos aumentam a superfície de ataque.
- Cada camada de validação de segurança adiciona tempo de processamento.
- Microsserviços foram criados para trabalhar em escala com múltiplas equipes. (Renan)
- Não faz sentido começar projeto novo com microsserviços sem domínio bem definido. (Daniel, citando Martin Fowler)
- Martin Fowler defende “Monolith First”; há artigo com contraponto no próprio site dele. (participante)
- O contraponto: se o domínio já está bem definido (ex: banco), pode fazer sentido começar com microsserviços.
- Equipe pequena (5 a 10 devs) usando microsserviços é over engineering. (Marcel)
- Exemplo: equipe de 6 pessoas na empresa de Marcel não justifica microsserviços.
- Microsserviços trazem overhead elevado: testes, desempenho, compreensão, gestão de equipes.
- A escolha de microsserviços também é organizacional, não só técnica:
- Lei de Conway: a estrutura do sistema reflete a estrutura da organização. (Renato)
- Uma equipe só raramente justifica microsserviços.
- Nubank como exemplo de evolução natural: software para 1 milhão de pessoas não é o mesmo que para 1 bilhão. (participante)
- Estilo mais comum e com o qual a maioria dos participantes teve mais contato. (Jack Nascimento)
- Características principais:
- Particionamento técnico.
- Comunicação feita de camada em camada.
- Camadas: apresentação, negócio, persistência, banco de dados.
- Deploy único.
- Separação de responsabilidades.
- Camada fechada: requisição desce de camada em camada sem pular etapas.
- Camada aberta: uma camada pode acessar outra não adjacente diretamente (ex: apresentação acessando persistência).
- Exemplo prático de camada aberta: camada de utilitários (utils) compartilhada entre camadas distintas.
- Camada fechada garante delimitação de contexto e consistência arquitetural.
- Deixar uma camada aberta deve ser uma decisão consciente, com justificativa clara.
- O livro menciona a regra 80/20: em algum momento, exceções acontecem; o importante é saber o porquê.
- Camadas lógicas podem existir em unidades físicas separadas ou na mesma unidade.
- Exemplo em aplicações web:
- Servidor de banco de dados: camada física de dados.
- Servidor de aplicação: camada de negócio e persistência.
- Navegador do usuário: camada de apresentação.
- Figura 10.2 do livro ilustra várias formas de organização física.
- Ferramentas de análise estática ajudam a manter a governança arquitetural.
- Exemplos por linguagem:
- Java: ArchUnit.
- .NET: .NET Architecture Tests.
- PHP: Deptrac (permite definir restrições arquiteturais via análise estática).
- Permitem garantir que regras arquiteturais sejam respeitadas e evitam erosão.
- Simplicidade: 5 estrelas.
- Modularidade: 1 estrela.
- Manutenção, testabilidade, deploy, evolução: notas baixas.
- Custo: baixo.
- Risco de over engineering: adicionar camadas só porque parece bonito, sem necessidade real.
- Exemplo: camada de serviço de aplicação (use cases) só faz sentido quando há múltiplos canais de API (web, mobile, IoT).
- Após surgimento de BFF e GraphQL, muitos deixaram de usar essa camada.
- Stack Overflow usou conexão direta com banco de dados no controller por questão de performance, e funcionou.
- A arquitetura deve se adequar à necessidade do negócio, volumetria e maturidade do time.
- Particionamento por domínio em uma única unidade de implantação. (Jack Nascimento)
- Evolução da organização em relação ao estilo em camadas. (Marcel)
- Ficou famoso com a palestra de Simon Brown, criador do modelo C4. (Marcel)
- Frameworks como NestJS são voltados para construção de monolitos modulares. (Jack Nascimento)
- Separar o código em escopos de negócio e fluxos de trabalho.
- Evitar chamadas cruzadas entre módulos:
- Módulos devem se comunicar de forma controlada, como departamentos de uma empresa.
- Um departamento não acessa diretamente os dados de outro sem um ponto de entrada definido.
- Ferramentas de análise estática (ex: Deptrac) podem controlar a comunicação entre módulos.
- Comunicação entre módulos pode ser feita via:
- Mediator (ex: MediatR no .NET).
- Sistema de filas (ex: Symfony Messenger no PHP, que persiste em memória, arquivo ou fila).
- Topologia de dados:
- Possível ter banco único com tabelas segregadas por módulo.
- Possível ter bancos separados por módulo.
- A regra deve ser explícita: cada módulo acessa apenas seu conjunto de tabelas.
- Reduz carga cognitiva da equipe. (Marcel)
- Facilita organização de equipes por módulo (Team Topologies). (Marcel)
- Facilita migração futura para microsserviços:
- Com domínios isolados e banco segregado, o microsserviço “já está pronto”. (Wesley, Roger)
- Casa muito bem com DDD e Clean Architecture. (Wesley, Marcel)
- Permite aplicar CQRS e outros padrões dentro dos módulos. (Wesley)
- É possível trabalhar com monolito modular + DDD + Clean Architecture + testes. (Marcel)
- Quatro tipos de times segundo Team Topologies:
- Equipe alinhada ao fluxo (stream-aligned team).
- Equipe facilitadora.
- Equipe de subsistemas complicados.
- Equipe de plataforma.
- Com monolito modular, é possível distribuir módulos entre times diferentes:
- Exemplo: 3 times de 3 a 4 pessoas, cada um responsável por 3 módulos.
- Equipe de plataforma provê capacidades transversais: logs, filas, SDKs.
- Equipe de subsistemas complicados cuida de módulos críticos (ex: checkout com otimização extrema).
- Melhor que arquitetura em camadas em engenharia e testabilidade.
- Deploy melhorou uma estrela em relação ao anterior.
- Peca em escalabilidade, elasticidade e tolerância a falhas (deploy único).
- Se falhar, é necessário reverter toda a aplicação, não apenas um módulo.
- Processamento em etapas: entrada, filtros sequenciais, saída. (Jack Nascimento)
- Cada filtro tem responsabilidade bem limitada e específica.
- Exemplos práticos:
- Step Functions com Lambdas na AWS. (Jack Nascimento)
- GitHub Actions / CI/CD pipelines. (participante)
- Ferramentas de ETL (Extract, Transform, Load).
- Se um passo falha, o processo inteiro é impactado (característica do estilo).
- Filtros não necessariamente bloqueiam o próximo passo; depende da configuração.
- Filtro do tipo transformador equivale ao map:
- Recebe lista de N elementos, retorna lista de N elementos transformados.
- Exemplo: lista de 5 usuários com nome e sobrenome, retorna lista com nome completo em maiúsculo.
- Filtro do tipo testador equivale ao reduce:
- Recebe lista, retorna valor único.
- Exemplo: lista de 5 nomes, retorna a quantidade.
- Funções são determinísticas: mesma entrada sempre produz mesma saída. (Marcel, confirmado por participante)
- Calcada no conceito de pipe (|).
- Criar programas pequenos que fazem uma coisa muito bem e podem ser encadeados.
- Cada programa é um processo com entrada (stdin) e saída (stdout).
- Exemplo: contar palavras, ordenar, remover duplicatas via terminal usando pipes.
- Marcel utilizava isso no dia a dia sem ter parado para refletir como estilo arquitetural.
- Custo baixo.
- Particionamento técnico.
- Simplicidade alta.
- Perde em manutenibilidade e tolerância a falhas (quanta: se quebra um, quebra o processo).
- Testabilidade mantida em relação ao anterior.
- Composta por sistema central (core) e componentes de plugin. (Marcel)
- Sistema central guarda a funcionalidade essencial e funciona de forma autônoma.
- Plugins adicionam funcionalidades sem alterar o núcleo.
- Exemplos:
- Eclipse: começou como editor de código e foi evoluindo com plugins. (Jack Nascimento)
- VS Code: adição de extensões de funcionalidades. (Jack Nascimento)
- Jira: sistema de plugins. (Pedro)
- Sistema de pagamentos com múltiplos métodos de pagamento plugáveis. (Wesley)
- Contra-exemplo: Chrome tem plugins mas não é microkernel porque o núcleo é muito grande. (Marcel)
- O grau de microkernelidade depende de quanta funcionalidade autônoma existe no sistema central.
- Quanto maior o núcleo, menos se enquadra como microkernel.
- Em tempo de compilação: contrato mais rígido, menos variabilidade.
- Em tempo de execução: mais flexível, similar à linkagem de bibliotecas dinâmicas em sistemas operacionais.
- Comunicação do sistema central com plugins pode ser feita via protocolo de rede.
- Plugin responde a um contrato (interface) definido pelo sistema central.
- Similar ao polimorfismo: implementações concretas respondem a um contrato.
- Exemplo do livro de Maurício Aniche (Solid): calculadora de impostos com método calcular e implementações para IPI, ICMS etc.
- Vantagem: se o plugin segue o contrato, pode ser plugado sem complexidade adicional.
- Iuri levantou a questão: microkernel parece com ports and adapters?
- Marcel: na hexagonal, cada porta tem funcionalidade distinta; no microkernel, os plugins tendem a fazer coisas similares (mas reconheceu dúvida).
- Wesley: no microkernel, o acoplamento ao núcleo é genérico; na hexagonal, é específico e codificado manualmente.
- Wesley: sistema central do microkernel funciona sozinho sem os plugins; na hexagonal, as portas e adaptadores são necessários para o fluxo completo.
- Pedro: a hexagonal resolve desacoplamento de tecnologias da lógica de negócio; o microkernel resolve extensibilidade de funcionalidades.
- Hexagonal: trocar Postgres por outro banco sem alterar a lógica de negócio.
- Microkernel: extensões que melhoram ou adicionam funcionalidades ao produto (ex: extensão de ad blocker no browser, timer pomodoro).
- Custo baixo.
- Particionamento técnico.
- Simplicidade alta.
- Modularidade reduzida em relação ao monolito modular.
- Manutenção: 2 estrelas.
- Escalabilidade, elasticidade e tolerância a falhas: reduzidas.
- Cuidado: plugins não devem se comunicar entre si para evitar acoplamento.
- “A arquitetura é sobre manter o software num estado consistente e coerente pra que a gente consiga evoluir ao longo do tempo sem muito percalços.” (Marcel)
- O livro não diferencia estilo arquitetural de padrão arquitetural de forma clara, o que é uma crítica legítima e merecia disclaimers explícitos em vários capítulos. (Wesley)
- Na era da IA, a degradação arquitetural acontece de forma exponencial: o que um dev levaria meses para degradar, a IA faz em dias se não houver guardrails. (Wesley)
- A IA só replica o que está no código e nas guidelines do projeto. Sem governança, ela age como qualquer dev desinformado. (Marcel)
- Todo software tende ao caos com o tempo; o papel do dev é retardar essa degradação, não evitá-la para sempre. (Renato)
- Microsserviços foram criados para resolver o problema de múltiplas equipes trabalhando em escala. Fora desse contexto, são over engineering. (Renan)
- O monolito modular é o “meio-termo” ideal: tem a organização do microsserviço sem os custos da distribuição. Quando o domínio está bem isolado, migrar para microsserviço é quase trivial. (Wesley, Roger)
- O código “grita” o modelo de negócio, não a tecnologia utilizada. (Uncle Bob, citado por Marcel)
- Pensar em módulos como departamentos de uma empresa ajuda a entender os limites de comunicação: um departamento não acessa diretamente os dados de outro. (Marcel)
- A filosofia Unix de pipes é um exemplo cotidiano de arquitetura pipeline que a maioria usa sem perceber. (Marcel)
- Microkernel e polimorfismo têm similaridade conceitual: plugins respondem a contratos, assim como implementações concretas respondem a interfaces. (Marcel)
- A diferença entre microkernel e ports and adapters: o microkernel resolve extensibilidade de funcionalidades; a hexagonal resolve desacoplamento de tecnologias da lógica de negócio. (Pedro)
- O sistema central do microkernel funciona de forma autônoma sem os plugins; na hexagonal, as portas e adaptadores são necessários para o fluxo completo. (Wesley)
- Quanto mais se avança tecnicamente, mais rápido e barato fica fazer as coisas do jeito certo. Boas práticas não são mais lentas para quem as domina. (Marcel)
- O conceito de P99 é uma ferramenta concreta para decidir se a latência de uma arquitetura distribuída está dentro do aceitável. (Wesley)
- Ferramentas de análise estática como Deptrac (PHP), ArchUnit (Java) e .NET Architecture Tests são formas práticas de automatizar a governança arquitetural. (Marcel)
- O livro fala sobre “o que você sabe que sabe, o que você sabe que não sabe, e o que você não sabe que não sabe”: o clube do livro serve exatamente para reduzir a terceira categoria. (Jack Nascimento)
- Estilo arquitetural
- Padrão arquitetural
- Big Ball of Mud
- Particionamento por domínio
- Particionamento por capacidade técnica
- Arquitetura em camadas
- Monolito modular
- Arquitetura pipeline
- Arquitetura microkernel
- Arquitetura hexagonal (ports and adapters)
- Falácias da computação distribuída
- Latência de cauda longa
- P99 (percentil 99)
- Stamp coupling
- Fitness functions
- Governança arquitetural
- Erosão arquitetural
- Entropia de software
- DDD (Domain-Driven Design)
- Clean Architecture
- CQRS
- Team Topologies
- Equipe alinhada ao fluxo
- Equipe de plataforma
- Equipe de subsistemas complicados
- Camada fechada
- Camada aberta
- Regra 80/20
- Deptrac
- ArchUnit
- Mediator
- Symfony Messenger
- Step Functions (AWS)
- Programação funcional
- Map / Reduce
- Filosofia Unix
- Pipe (Unix)
- Espectro da microkernelidade
- Simon Brown
- Modelo C4
- Monolith First (Martin Fowler)
- Lei de Conway
- Over engineering
- Trade-off arquitetural
- Análise estática de código
- Guardrails
- Acoplamento
- Coesão
- Topologia de dados
- Deploy único
- Arquitetura distribuída
O terceiro encontro do Clube do Livro da Leitura Dev cobriu os capítulos 9 ao 13 do livro “Fundamentos da Arquitetura de Software” de Mark Richards, com foco nos fundamentos dos estilos arquiteturais e nos estilos arquitetura em camadas, monolito modular, pipeline e microkernel.
A discussão foi rica e técnica, com participantes de diferentes níveis e contextos. Um dos temas centrais foi a falta de clareza do livro na distinção entre estilo arquitetural e padrão arquitetural, crítica levantada por Wesley e endossada por Marcel e outros. O grupo chegou a um entendimento prático: estilos são mais amplos e descrevem a estrutura geral de uma arquitetura, enquanto padrões resolvem problemas específicos dentro de um estilo.
O conceito de Big Ball of Mud gerou uma discussão relevante sobre governança, degradação arquitetural e o papel da IA no processo de desenvolvimento. O consenso foi que a IA amplifica tanto boas quanto más práticas, tornando a governança arquitetural ainda mais crítica. Ferramentas de análise estática como Deptrac, ArchUnit e .NET Architecture Tests foram apontadas como mecanismos concretos para automatizar essa governança.
As falácias da computação distribuída abriram um debate sobre quando microsserviços fazem sentido. O grupo convergiu para a visão de que microsserviços são adequados para organizações com múltiplas equipes e domínios bem definidos, e que equipes pequenas raramente justificam esse overhead. O monolito modular foi apontado como o estilo mais equilibrado para a maioria dos contextos, por combinar organização por domínio com a simplicidade do deploy único e facilitar uma eventual migração para microsserviços.
O próximo encontro está agendado para o dia 5 de agosto, às 19h30, e cobrirá os capítulos 14 ao 16. Antes disso, na semana seguinte, haverá um encontro especial com o convidado Erick Aceiro, autor do livro “De Valor” (Casa do Código), para uma conversa livre sobre arquitetura de software. Em setembro, está previsto um evento presencial na Oracle em São Paulo para celebrar a finalização do livro.