Olá, sejam todos muito bem-vindos a mais uma aula. E na aula de hoje, a gente vai olhar agora com carinho a parte de dentro do hexágono. A gente vai criar os nossos domains lá dentro. E talvez, ou nessa ou na próxima aula, a gente crie a interface de repository para a gente finalizar tudo que está dentro do nosso hexágono e depois ficar só a parte dos Dreamers para a gente fazer o ajuste. Então vamos lá, o que a gente vai fazer aqui? A gente vai trabalhar agora nas entidades do domínio. A gente veio bastante nos use cases, mas a gente ainda não trabalhou nas entidades do domínio. O nosso caso de nos use cases, mas a gente ainda não trabalhou na quantidade de domínio. O nosso caso de uso, por exemplo, do create customer, ele não fala aqui muito domínio, ele está usando um tal de customer que é uma tabela que está lá em infraestrutura, então não é bem o domínio que a gente está querendo chegar. A gente quer chegar num domínio que faça sentido e não num domínio que representa só tabelas de bancos de dados. Vocês já estudaram bastante sobre o DDD aqui no MBA. E lá no DDD a gente conheceu o conceito de agregado. E quando a gente passa a falar mais de domínio ao invés de infraestrutura, a gente enxerga, principalmente aqui no hexagonal, que esse conceito do agregado faz muito sentido. Eu não preciso necessariamente ter uma interface de repository para cada entidade ou para cada tabela. Não preciso pensar em tabela. Eu penso no agregado, que é um limite transacional das entidades que estão ali dentro dele. Ou seja, é uma classe que pode ter dentro dela outras entidades. Ela mesma é uma entidade porque ela é identificada por um ID, e não é um objeto de valor que é identificado pelas suas propriedades. E o agregado é a entidade maior que agrega N entidades, pode agregar N entidades, que todas elas precisam estar íntegras e com as suas invarianças, de maneira que as invariantes não quebrem justamente pela falta de integridade no agregado ou por falta de informação. Então, tudo que precisa ser persistido de maneira junta e atômica tem que estar dentro do mesmo agregado. Se pode ser persistido e atualizado independente, talvez esse cara seja um outro agregado à parte. Um pouco confuso, mas a gente vai entender aqui na prática se você ainda não entendeu no decorrer do curso. Então vamos lá. Outro ponto importante. Lembra que eu falei que o Alistar Coburn não entra dentro do detalhe do hexágono? Ele não fala quais os packages usar aqui dentro, qual estrutura de pastas usar. Porque a estrutura de pastas é muito genérico para cada projeto, para cada linguagem E para ele não importa muito Para ele o que importa é que você proteja o seu hexágono dos drivers e dos driven actors Os atores de maneira geral Então no hexagonal, como a gente está trabalhando com dois packages grandes O de application e o de infrastructure No hexagonal eu gosto de trazer um package aqui para dentro Chamado de entities Esses entities, por sua vez Pode ser visto como um domain Um package de domain em algumas outras arquiteturas Como, por exemplo, na do CleanArch Na do CleanArch eu gosto de criar o package de domain E aqui fora E aí no domain ter só as entidades de negócio mas como no hexagonal a gente está tentando proteger o hexagonal e o hexagonal é uma coisa só eu gosto de deixar aqui dentro tá bom então vamos lá o que a gente vai fazer aqui a gente vai criar a primeira entidade que é a entidade de Customer. Ela, vamos por enquanto fazer ela do que a gente já viu aqui no mundo do DDD. Que ela tem, por exemplo, um objeto de valor que é o seu identificador. Lembra que no DDD a gente sempre identifica um agregado e uma entidade através de um ID E a gente tende a criar classes para esse identificador Objetos de valores para esse identificador Então a gente vai fazer aqui da mesma maneira Vamos criar um Customer ID E vamos colocar aqui como Customer ID Beleza, então eu vou criar aqui dentro uma outra classe. Essa classe eu não vou criar uma base class aqui de objeto de valor, não é o objetivo ainda. E esse customer id, na verdade, ele está mapeado como id long, só que ele não vai ser um long, ele vaii uma RecordClass, afinal objetos de valor são imutáveis E ele é representado igualitariamente pelo seu valor, que no caso aqui é um ID O que eu vou fazer aqui para ajudar? Eu vou criar um FactoryMethod para ajudar a criar e para ajudar a construir esses objetos de valor Então o primeiro deles vai retornar um customerId e ele vai se chamar unique. Ele vai retornar um newCustomerId que é o uid.random.beleza. O segundo deles é um chamado customerId chamado, customer ID, chamado with. Esse with, ele vai receber uma string, né? Ou seja, ele vai receber um value, vai converter esse value para um id e vai setá-lo. E aqui é importante a gente até já adicionar uma tratativa, né? Porque pode ser que esse cara seja um valor inválido. E se for um valor inválido, vai ser lançada uma argument exception e a gente converge para uma validation exception, que é invalid value for customer ID. E aí show de bola, a gente já tem o nosso identificador aqui, e aí por enquanto a gente vai trazer o name a gente vai trazer de maneira simples aqui para depois a gente trabalhar melhor na validação, porque daí vocês vão ver que bacana que vai ser a evolução aqui então o name a gente tem o cpf e a gente tem o string e-mail. E o que a gente vai trazer aqui? Vamos trazer já de cara um construtor. Final, final, final, final final aqui no consultor a gente deve adicionar as verificações então se o customer id for igual a nulo throw new validation exception então invalid customer ID for customer. A gente pode verificar, por exemplo, as outras propriedades, if name, name igual a nulo, a mesma coisa. Então, a gente está protegendo o nosso customer de receber valores inválidos. protegendo o nosso customer de receber valores inválidos. Invalid name for customer, age for customer. Vamos validar isso aqui. CPF, CPF. E aí, por último, o e-mail e o e-mail. E detalhe, né? Tipo, dá para ir além. A gente a gente pode por exemplo validar aqui ó se o cpf é válido a gente pode colocar uma reggaex básica para validar o cpf e ó não vou nem não vou nem fazer na mão essa reggaex aqui eu copiei já pronto a internet mesmo depois podem ver é aí no no código comitado essa mesma reggae e primeiro a mesma coisa vamos utilizar aqui uma reggae já pronta e esse cpf obviamente estava sendo uma avaliação bem dame nessa tá validando é que ele ele está pontuado corretamente e aqui só está validando que o e-mail tem as características básicas, não tem muito segredo aqui. E aí aqui também a gente vai fazer os Factory Methods que vão nos ajudar a construir uma entidade de Customer. a gente vai expor dois o primeiro melhor a gente pode expor dois mas no caso como a gente vai deixar o construtor público vamos expor apenas um que é o do ou create tem gente que gosta de chamar create e recebe os parâmetros ou eu gosto de chamar new customer que diz mais sobre o que ele está tentando fazer, que é de fato criar um novo customer. Então, string name, string cpf, string email. E aí, return new customer com o customer id aqui unique, com o name c aqui e o nick, com o name, cpf e o email. E aí depois a gente pode adicionar, por exemplo, os getters, tá? Getter para tudo isso aqui. E setters não faz muito sentido a gente adicionar, porque a gente não vai manipular esse cara, a gente não quer trazer um domínio anêmico para o nosso customer a gente quer manipulá-lo através de métodos que expõem comportamento justamente para isso o encapsulamento e não simplesmente um bando de sete aqui beleza já temos a nossa entidade aqui, agora o que a gente pode fazer para proteger, para terminar de proteger o nosso domínio? Aqui no caso de uso, a gente vê que ele está trazendo um customer service, e o customer service não tem a ver com o nosso exágono, com o nosso caso de uso. As maneiras da gente expor uma interface para o mundo afora, normalmente são através de duas formas. A primeira, expor um repository, que é o padrão repository, que vocês já viram aqui no MBA, no DDD, que vocês já viram aqui no MBA do DDD que é basicamente uma interface que expõe que gerencia a persistência de um agregado e é um agregado, não uma tabela igual a gente tem aqui os repositórios do Spring a gente tem os repositórios aqui mas eles mapeiam um para um com tabelas e não é necessariamente isso que a gente quer a gente quer para agregados A gente tem os repositórios aqui, mas eles mapeiam um para um com tabelas. E não é necessariamente isso que a gente quer. A gente quer para agregados. E a segunda maneira de expor é através de gateways. Então, os gateways, eles têm o padrão de gateway, table gateway, data table gateway, se não me engano. Mas também tem o padrão gateway que é para expor uma interface que a gente sabe que vai ter uma ponta cujo driver é um HTTP, cujo Driven Actor é um HTTP API para um outro serviço. Qual que a gente vai precisar aqui? Vamos trazer os repositories. Repositories. Então, ó, mais um packagezinho. E esse cara vai ser uma interface. Que é CustomerRepository. Esse cara, por sua vez, não vai estender de nada. Mas vai expor alguns métodos. por sua vez não vai entender de nada mas vai expor alguns métodos então a gente vai expor um a customer of id por exemplo esse cara retorna um customer e ele recebe um customer id vai ter um ele retorna na verdade um optional, né? Um optional de customer. Opa, de customer. E é o customer aqui da entidade dentro do application, tá? Muita atenção para não confundir com o que tem lá. O que mais que a gente precisa? Para CPF e confundir com o que tem lá. O que mais a gente precisa? Para cpf e para email, então vamos lá, customer of cpf, e aqui por hora é um string cpf, a gente nem tem esse E aí o CustomerOfEmail. E-mail. E-mail aqui. Depois a gente tem o método Save. O método Save pode retornar o próprio Customer ou poderia ser Void. É que na verdade nem é Save, né? Create. Vamos diferenciar os dois. Então, customer e update, no caso, a gente nem vai ter aqui, mas... Ok, a gente nem tem caso de uso que atualiza, mas vamos colocar. Beleza, então já temos o nosso repositório aqui. Esse repositório, ele é do customer como um agregado, e não o customer como tabela abaixo nível que a gente estava trabalhando até agora. Consequentemente, já temos o nosso agregado, já temos o nosso repositório, a gente vai vir aqui no Create Customer e vamos substituir esse cara. Vamos trazer o do Application, que é a interface alto nível e aqui vamos mudar vamos mudar vamos mudar e aqui a gente vai mudar também aqui a gente vai corrigir a custom repository customer of cps custom repository customer of email aqui o customer a gente já sabe que a gente mudou, então agora é, ele ainda está apontando para o antigo, então a gente tem que ajustar, vamos remover esses dois, e vamos importar o customer novo, e aí a gente vai utilizar aquele FactoryMethod que a gente criou. Então, newCustomer, input.name, input.cpf, input.email. Esse new morre, isso aqui morre. E a gente, na verdade, pode passar direto para o save aqui. Que é o customerRepository.create. E o var vir vir para cá. Olha como já encurtou bastante o nosso caso de uso, né? Por quê? Porque agora a gente está trabalhando com, a gente está distribuindo a lógica, né? Que não vai ficar, e aqui é o ponto value e o output mudou porque agora ele é um UID a gente estamos distribuindo a lógica que estava grosseiramente lá no controller distribuímos um pouquinho para o caso de uso agora do caso de uso a gente distribui um pouquinho para a nossa entidade de domínio que ela passa a saber se auto validar na hora de se construir ok? então vamos lá a gente mudou aqui o nosso querido caso de uso deixa eu ver quanto tempo de aula, beleza a gente até pode já mudar o do getCustomerById também então vamos já mudar os dois repository customerRepository customerRepository e aqui é o repository.customerOfID e aí aqui repository.customerofid e aqui customerid.if ele é um long então vamos mudar o input para uma string e vamos retornar customerid.value que aqui também passa a ser um uid, cpf, email e name. Aqui tá um uid, mas a gente pode retornar como uma string também, tá? E aí a gente usa aqui, ó, toString. E aqui vamos fazer a mesma coisa, vamos retornar string aqui ponto toString beleza, já mudamos os dois, agora a gente todos os import, todas as dependências são internas do hexágono aqui também, todas as dependências são internas do hexágono, só que é claro que alguns testes vão quebrar. Então, vamos começar a corrigir. Customer resolver. O customer resolver... Ah, tá. Vamos mudar aqui para string. Customer controller string. E aqui, claro que ele ainda não vai ter então por enquanto a gente vai deixar nulo aqui porque ele tem uma dependência que ainda não existe então vamos deixar nulo, to do fixdependency e vamos adicionar aqui um fix dependency depois. Agora a gente tem que corrigir os testes, os testes aqui unitários. Então o teste do create customer. Vamos lá. cpf, email, name, papapá. Aqui agora é um Customer repository E olha que interessante Esse cara A gente pode continuar trabalhando com o Moquito Mas a gente já pode facilmente Criar um stub dele Na verdade nem um stub Uma implementação fake dele Seria possível a gente criar uma implementação fake Como que seria essa implementação fake? Vou mostrar aqui para vocês, para vocês verem que interessante. Por ser uma interface, a gente poderia static class em memory customer repository que implementa customer repository e aí traz todos os métodos dele vamos fazer o seguinte para não ficar com esse import tosco já vamos tirar aqui isso aqui tudo que é infrastructure a gente sabe que vai morrer então vamos continuar aqui, vamos trazer o customer, aí o customer já vai ser importado corretamente e o que a gente pode colocar aqui, por exemplo, private, final, ele vai ter um mapa de string para customer. Então, customers. A gente, como busca por CPF e busca por e-mail, a gente poderia ter um customers by CPF e customers by e-mail. e customersByEmail e aí a gente cria essa nossa classe optional por id, então optional of nullable dis.customers.getAnId.value e esse aqui a gente né só para proteger o objeto o objeto ponto e quais não no ai no ano aí de que está reclamando aqui, ah tem um cara mais, beleza, aqui embaixo a mesma coisa, só que aqui vai ser o do por cpf, e aqui é cpf, e aí não tem o Valley. E aqui embaixo, a mesma coisa. Só que aqui é o por e-mail. O create, né? O create aqui, a gente vai fazer, ó. Dis.customers.put.customer.customerid.valley.customerid.value e customer Ah tá, então aqui tem que ter o toString Aqui é e-mail Beleza E aí vai retornar customer Só que aqui tem que preencher os outros também, né? Então o por cpf aqui é por cpf e por e-mail e aqui é por e-mail e o string é redundante beleza, vamos lá estamos quase lá, já estamos para corrigir esse nosso esse nosso customer aqui. E o que a gente pode fazer aqui agora? O update é a mesma coisa, né? Então, vamos colocar aqui, ele vai sobre escrever, tudo bem. Agora que a gente tem esse cara em memória, a gente pode utilizar aqui, ó. Ao invés de ser esse mock, vai ser o new in memory. E também não é mais customer service, então a gente vai customer service customer repository, beleza. Esses mocs não faz muito sentido ter mais aqui também ó, aqui também a gente não precisa mocar, é isso. Está vendo? Tiramos tudo que era mock para ficar uma implementação em memória. Aqui não deve cadastrar um cliente com CPF duplicado. Ou seja, a gente pode instanciar esse cara aqui mais para cima. Aqui nesse de baixo a gente pode instanciar ele aqui por exemplo o customer a gente sabe que tem que ser através do new customer onde a gente passa o expected name expected cpf expected e-mail e a gente pode passar ele um pouquinho mais aqui pra cima pra gente chamar o custom repository.create então assim a gente já criou esse customer a gente não precisa disso aqui show de bola agora a gente vai lá por debaixo a gente pode trazer a mesma coisa aqui, isso aqui tudo é vala, e aí customer.createACustomer, e aí só trazendo o input aqui mais para baixo. Isso aqui não precisamos mais, customer repository, e olha lá, vamos rodar esse teste, ah tá, o do getbyed também está incompatível, então a gente pode fazer a mesma coisa aqui, vamos fazer o seguinte, vamos externalizar esse cara, vamos extrair esse cara, refactor, extract inner class to another class não tem como, cadê, cadê create new squash file from Caralho. Refact Generator Generator Bom, vou fazer o seguinte. Vou fazer na raça aqui, tá? Vou deixar aqui na... Vamos lá no teste. Vou deixar aqui no application esse carinha aqui ó, in memory repository e vou trazer tudo aqui pra cá E aí eu vou importar esse cara. Aí, beleza. Agora a gente consegue usar esse cara lá no GetById também. Então, aqui onde está mocado, não preciso. Vou usar o repository. A diferença é obviamente preciso fazer ele aqui mais pra cima tudo que tem infraestrutura a gente já remove a new customer ponto new customer expected name expected cpf, expected email, isso aqui morre também, esse custom a gente vai passar pro create aqui e aí o expected id aqui, ele é uma string, a gente tá new random.toString beleza, e aí nesse aqui, no de baixo, na verdade a gente só precisa disso aqui .toString Beleza E aí nesse aqui, no de baixo Na verdade a gente só precisa disso aqui Ao invés de ter tudo isso aqui A gente só precisa disso aqui E esse cara é o toString Show de bola Vamos rodar agora todos os testes Aqui do nosso Do nosso layer de application O que a gente está quebrando aqui event controller test event repositori que o cara quebrou a índia vai aí de recorde requiredString... Ah tá, é porque agora o nosso caso de uso aqui tá retornando uma string e não mais o long porque ele tá criando um UID na mão, né? Então, na verdade nem era pra ter impactado, né? Create event aqui, ó. Estava errado o output. Por isso, mas talvez os testes do customer falhem. Ok, os integrados falharam, obviamente. Mas o... Cadê? Ah, esse aqui. Invalid cpf for customer, olha lá. Então caímos numa validação, essa validação aqui, esse cpf ele está inválido porque ele está sem ponto, e lá está testando ponto então vamos copiar isso aqui, onde, tudo que tiver aqui a gente, sem entrar no mérito, se estava com máscara ou sem máscara, tá? Mas aqui é título de aula prática mesmo. Então vamos lá. Vamos rodar de novo. Mas todos os casos de uso fora os integrados deveriam passar Invite CPF for Customer. Bacana. Invite CPF for Customer. Bacana. Invite CPF. Vamos lá ver. Como é que está essa nossa regex de CPF. Create new customer. 333-2. DDD. Talvez. DDD talvez vamos ver o que está acontecendo aqui tá, entendi o que está acontecendo aqui é na verdade isso aqui a gente não precisa ter esse barra barra aqui no Java então vou mudar de novo, vou rodar só o createCustomer, beleza, o createCustomer passou com sucesso, vou rodar então é .customerId.value provided.toString colocar isso aqui para uma variável expectedId finalVarExpectedId agora sim vou mudar todos os testes e deve obter um para create expected id para o input e o customer.getById, customer.getById, que é o Wifi ID, cad for lá no inmemory, vamos ver, o custo-verse tem um cara que éa aí, ponto value, ah claro, o nosso get aqui tá inválido, na verdade era isso que tava reclamando, ponto toString, agora sim, o tipo tava diferente. o tipo estava diferente. Beleza. Então, ó, obviamente, ainda tem coisas quebrando, mas a gente já começou a trazer um pouco de expertise, um pouco das regras de negócio para dentro do nosso hexágono. A gente já criou aqui o nosso customer, colocamos até um pouco de validação, que até então nem tinha. Validação de cps, validação de e-mail. Criamos o identificador do Customer aqui, mudamos para o ID, criamos o repository. Então, a gente já está começando a trabalhar com umas coisas bem legais aqui. E detalhe é que, dentro dos nossos casos de uso, não tem mais dependência externa do hexágono. Então, estamos avançando bem, estamos aprendendo muita coisa. Espero que vocês estejam gostando. Vejo vocês na próxima aula.