Olá pessoal, tudo bem? Sejam todos muito bem-vindos a mais uma aula. E na aula de hoje a gente vai entrar, deep dive, vamos entrar fundo nos conceitos da arquitetura hexagonal. Hexagonal Architecture. Conhecida também como Ports and Adapters, é um alter name aí que também é oficial e funciona. O criador do hexagonal architecture, o Alistair Coburn, ele costuma dizer que o nome que pega, o nome que vende é hexagonal architecture, mas o nome técnico mesmo é ports and adapters. Então, vamos lá. O que o Alistair Coburn quis quando ele propôs esse tipo de arquitetura de aplicação? A frase da publicação dele, que gerou efeito, a frase de efeito com que ele quis resumir tudo em um único tweet, é essa. Permita que a aplicação seja igualmente dirigida por usuários, programas, testes automatizados, batch scripts, e seja desenvolvida e testada de maneira isolada dos eventuais dispositivos e bancos de dados. O que ele quis dizer com isso? Ele quis dizer para que a gente foque no core da aplicação, que a gente consiga desenvolver, sem que a gente fique travado em ter algum tipo de acoplamento com a parte da saída, com bancos de dados, com algum tipo de acoplamento de máquina, de file system, qualquer tipo de dispositivo que possa gerar algum acoplamento. E também o de entrada, que a gente não acople o nosso código ao mecanismo de entrada da nossa aplicação. Então, seja ela REST API, seja ela um GraphQL, seja ela um usuário, um teste automatizado, qualquer coisa que entre, nosso código não deve ficar acoplado a ele, tampouco algum dispositivo de saída. A gente vai ver com mais detalhes o que significa isso. Mas antes, acho que é importante a gente voltar, vamos dar um passo atrás, e a gente vai entender o que a gente quer resolver com esse tipo de arquitetura, que na verdade hoje é conhecido como Domain Centric Architecture, né? É a arquitetura que volta a fortalecer o domínio da aplicação e não o framework e a biblioteca, né? Mas então vamos lá, o que a gente está querendo resolver mesmo? Big Ball of Mud. Big Ball of Mud é um anti-pattern que talvez você já tenha trabalhado em algum projeto que tenha as características de um Big Ball of Mud. Algum projeto que de fato se tornou uma bagunça, você encontra uma mistura de espaguete e code, um código mal elaborado, um código às vezes muito estrutural para algo que é orientado a objeto, um código com um domínio anêmico, ou seja, as entidades não encapsulam nada, não tem comportamento, só um bando de pojos, getters e setters um código onde está cheio de middleman que é uma classe que simplesmente não faz nada só faz um proxy para algum outro lugar um código que é difícil de testar um código que é difícil de dar manutenção um código que é difícil de testar, um código que é difícil de dar manutenção, um código que adicionar funcionalidades novas é custoso, um código que você consegue identificar claramente um projeto que está sofrendo disso, quando você fala para os desenvolvedores para atualizar, adicionar ou remover alguma feature desse projeto e eles olham com aquela cara de, hum, melhor nem mexer aí, né? Se aconteceu isso, muito provavelmente é porque aquele projeto já passou pela mão de vários desenvolvedores, dos mais juniors ao mais seniors, cada um tentou implantar um tipo de arquitetura e no fim das contas saiu isso. Algo intestável, complexo de mexer, sem padrão nenhum. E por que eu estou trazendo esse problema? Significa que a arquitetura hexagonal vem para resolver esse problema específico? não exatamente mas o que causa esse problema é justamente você trabalhar com uma arquitetura de aplicação que não é bem elaborada e que não te diz exatamente o que fazer ou como organizar as coisas como separar as coisas de maneira coesa que é o clássico MVC que é o clássico que é um padrão no caso que é o clássico arquitetura em camadas onde você tem lá a camada de interface de usuário camada que também é conhecida como presenter, depois a camada de services tem lá a camada de interface de usuário, camada que também é conhecida como presenter, depois a camada de services, depois a camada de repository, de data persistence. Qual é a granularidade que esse tipo de padrão nos informa, nos diz para a gente criar uma service, por exemplo? Qual é o critério para a gente criar uma service, por exemplo. Qual é o critério para a gente criar uma service? Ou uma controller? Ou um repositório? O que define que vai ter uma service? É uma entidade? Cada entidade tem uma service? A gente pode cair no problema onde a gente tem Services gigantescas Um bloater Ou então, não, vamos quebrar Por Caso de uso Cada caso de uso é uma service Ou vamos quebrar Por métodos Que representam alguma coisa O padrão não diz o que fazer E esse tipo de coisa acaba levando pro Big Ball of Mud. Se você não acredita em mim, eu vou te mostrar aqui um código. Pra começo de conversa, essa já é a estrutura de pasta. Artesura em camadas, disseram. MVC, disseram. E aí acaba acontecendo isso. E esse é o código de uma aplicação real. disseram, né? MVC disseram. E aí acaba acontecendo isso. E esse é o código de uma aplicação real. Se você está imaginando aqui, ah, putz, não acontece. Não, acontece sim. Esse é o código de uma aplicação real e essa aplicação é a aplicação core da minha startup, chamada Contrata Show. Zero vergonha de mostrar o código aqui. Zero vergonha de mostrar o código aqui, zero vergonha de mostrar. Fui eu que produzi esse código? Algumas partes, sim, mas não nasceu comigo a aplicação. Não vem ao mérito julgar se o cara era um bom desenvolvedor ou ruim. Inclusive o cara era muito bom, só que era startup. O que a gente queria era validar a ideia, e apesar dele ser um bom desenvolvedor, ele é um desenvolvedor meio Go Horse, mas independente, não é essa a questão, é que é uma startup que já vai dar ideia, começa a colocar código lá, depois a gente refatora. Só que o depois a gente refatora é importante, precisa ter esse depois a gente refatora, só que o depois a gente refatora é importante precisa ter esse depois a gente refatora se não a gente fica na big ball of mud cada pessoa tentando implementar um padrão implementar um jeito diferente de fazer porque começa a ficar tão ruim que vem os pequenos soldados que vem o herói, o herói tenta fazer uma coisa diferente daí ele não consegue, só piorou aí vem outra pessoa tenta fazer uma coisa diferente daí ele não consegue, só piorou. Aí vem outra pessoa, tenta consertar, só piora. Então, antes que o código fique grande demais, é importante que ele seja corrigido. E isso é um exemplo. O exemplo dessa aplicação real da startup Ela ainda tá assim, a gente ainda tá corrigindo, né? Aos poucos, mas, ó Pelo fato do padrão arquitetural não dizer Como isolar melhor as coisas Como separar melhor as pastas Olha no que ficou Tem uma pasta controller Tem uma pasta domain TO, enums, exception, form Job, JWT, Enums, Exception, Form, Job, JWT, Service, Security, Specification, Utils, AWS, Audit. O que é isso? É Feature? Folder? É camada? E vai além, tá? Olha Services, por exemplo. Olha o nível das Services aqui. Eu tenho... Lembra que eu falei que o padrão não especifica a granularidade das services? Eu tenho aqui, por exemplo, agrupador. Isso nem service é. Não escrevi porque está aqui. Aí eu tenho alguns services grandes, que talvez representem algum agregado, uma entidade. Então, analytics, services. Eu tenho um command perdido aqui. Avaliação contratante. Então, chat, cidade, taxa de fechamento, closing rate, dispositivo. Eu tenho algumas services grandes até, mas aí de repente eu venho aqui e aparece, por exemplo, notificação usuário, notificar artista, novo usuário, tem cara de feature, tem cara de, talvez, um caso de uso, talvez algo muito específico, muito granular, push service, SMS validation service. Então, o código se perde, o padrão não impõe nada. Cada um faz o que quer. Não tem um guidance, um guia. Como será que está o código? Porque a gente só falamos das pastas. Que talvez ainda nem represente tanta coisa. Mas vamos ver o código, como é que está? Banda Controller. Esse é o controller de cadastro da banda, do artista. Olha quanta dependência tem. Não vou nem contar, mas deve ter umas 20 aqui. Ou próximo disso. Olha quanta dependência tem. Qual é a chance de não ter uma dependência cíclica aqui? Ou seja, uma uma uma classe depende de outra classe, depende de outra classe, depende da primeira. Qual é a chance de não ter? Olha o método do controller. Translational no método do controller, recebe informações de informações HTTP e aí tem toda a lógica aqui. Instancia a entidade banda, recebe informações de... informações HTTP, e aí tem toda a lógica aqui, ó, da instancia, entidade, banda, seta um monte de coisa aqui, busca dados, né, ou seja, tudo que é repository aqui, a gente pode imaginar que é uma query, né, uma query sendo feita aqui no controller. Compara senhas, né, validação, páginas... chama uma service para criar página para esse cara, depois chama um repositório para persistir, depois cria um usuário, depois salva e seta a senha. Quanta responsabilidade não tem esse cara? Quanta coisa ele não faz? E se depois eu quiser expor essa minha aplicação para um GraphQL, por exemplo, que não está vindo de REST? Eu vou ter que copiar o código? Vou copiar tudo isso, vou colar lá no render do GraphQL, ou eu vou mover para uma service, uma refatoração grande. Olha o update. É igualzinho, uma refatoração grande. Olha o update. É igualzinho. O update é igualzinho. Muda uma coisa ou outra. Mas olha o update. Está vendo que esse tipo de código acontece? E aí na prática, o que a gente tem? Já ouviu falar no conceito de pirâmide de testes? de pirâmide de testes? A pirâmide de testes, ela basicamente nos diz, é basicamente uma forma de mostrar a quantidade de testes por tipo que uma aplicação deveria ter. Aqui é uma pirâmide bem resumida, mas o que é importante é que a base deveria ser forrada de teste unitário. Testes unitários são aqueles testes rápidos de serem executados, são testes focados em uma unidade. Depois a gente tem uma camada boa de integration test que são testes que dependem de framework que podem ser um escopo maior, broad ou um escopo menor, mais narrow que é basicamente, depende do framework mas estou testando uma única camada estou testando a camada REST do framework estou testando a camada de persistência ou mais broad, estou testando um método inteiro do do meu framework, uma service inteira, um método inteiro do meu framework uma service inteira um método da service inteira, por exemplo e aí acima ainda tem os testes manuais, que a gente chama de teste de regressão que você escolhe a aplicação e vai lá tentar cadastrar, tentar atualizar fazer uns testes na mão idealmente era para ser assim o projeto para otimizar, fácil de testar, ganhar performance. Mas o que acontece é que a gente acaba caindo nessa pirâmide invertida aqui. Onde você tem muito teste manual, o teste manual é o teste que a gente confia. Então fez uma mudança, sobe a aplicação, vai lá no Postman, começa a testar, vai lá na interface, começa a testar, aí depois você tem uma espessa camada de teste integrado, que demora pra caramba pra passar, pra executar, a pipeline fica lentíssima, e aí lá na pontinha uns testes unitários, aí tô testando um útil unitariamente, tô testando um encapsulamento Às vezes de um pojo Estou testando alguma coisa unitariamente Alguma coisa bem bocadinha Algo que não fica bom Não fica bom Então Com base nisso Um querido Estudioso Chamado Martin Fowler Se você não conhece, recomendo quem tem o site dele, tem muito post muito bom, ele é da ThoughtWorks. do design stamina hypothesis. A hipótese da stamina de um bom ou mau design. O que esse gráfico basicamente diz? A gente tem aqui o tempo, eu tenho o x, eu represento o tempo, e o y aqui representa a quantidade de funcionalidades acumuladas. E a gente tem duas linhas, a linha onde o projeto foi para um bom design, a linha onde o projeto foi para um mau design, ou sem design algum. E o que ele diz é basicamente assim, abaixo de uma certa linha de corte, E o que ele diz é basicamente assim, abaixo de uma certa linha de corte, abaixo dessa linha aqui, vale a pena, na verdade pode valer a pena você nascer sem pensar no design. Então uma startup, por exemplo, pode valer a pena abaixo dessa certa linha aqui, de uma quantidade X de funcionalidades, de você não se preocupar com design e sim preocupar com time to market. Começa a codificar, vamos testar a ideia, vamos fazer acontecer, vamos levar em produção. Depois dessa linha, se você seguir pelo caminho de não parar, arrumar a casa e continuar sem design, você eventualmente não vai conseguir ter agilidade para entregar funcionalidade. Você vai cair no enigma of mud que a gente comentou. Vai ser muito custoso adicionar as funcionalidades novas até o momento em que o projeto morre porque ninguém quer adicionar funcionalidade nova, não é produtivo, não funciona. Então, passando essa linha, é bom que você dê uma pausa, arrume a casa e aplique um bom design. Porque no bom design você continua numa progressão muito boa você continua adicionando funcionalidade no mesmo tempo que leva tempo e isso acontece acontece, tenho 10 anos de vivência em design software de carreira e já vi muito isso acontecer. Então, qual que é a proposta? Vamos fazer uma pausa aqui no vídeo e no vídeo seguinte a gente vem com a proposta que é basicamente a arquitetura hexagonal. Até breve.