Olá, sejam todos muito bem-vindos a mais uma aula e vamos dar continuidade aqui ao modelo, a modelagem das nossas entidades, que na verdade a gente nem pode chamar de entidades, acho que agora a gente chama de domínio mesmo, e como a gente está aplicando cada vez mais o DDD, ele já tem os value objects, agregregados os agregados são entidades esse nome entities ele está ficando cada vez mais fraco acho que a gente até pode aproveitar e renomeá-lo para domain o que vocês acham? porque dessa forma fica mais coeso agora agora fica mais coeso que a gente aplicou um ddd justamente porque a gente aplicou um DDD justamente porque a gente tem objetos de valor agregados, entidades a gente não tem só índices então acho que agora aqui faz mais sentido ficar como domain mesmo mas dando continuidade aqui então, faltou agora a gente mapear o subscribe customer to event, né event a gente fez o do create event agora a gente tem que fazer o do subscribe customer então bora lá o que a gente vai fazer aqui tem um detalhe, eu acho o create event, a gente ainda não passou o o que a gente tinha lá como spots reservados. Então, eu estava com uma ideia de fazer o lance de spots reservados totalmente assíncrono, mas a gente pode sim ter uma referência aqui, depois eu mostro a vocês como a gente pode fazer de maneira assíncrona, acho que vai ficar bem legal o conteúdo e uma outra coisa que também na verdade antes de continuar pro ticket uma coisa que eu vim incomodando bastante é essas validações aqui no construtor, que no fim essas validações podem até acabar ficando duplicadas então o que eu recomendo que a gente faça aqui, vamos é um por vez o customer não faz não faz muita diferença tá bom é o id no caso não faz muita diferença porque o id é imutável depois que ele construiu mas coisas como name cpf email a gente pode sim ter aqui o set a gente não tem no gaza porque a gente não tem o update do customer mas para vocês aprenderem a gente pode ter aqui por exemplo o set cpf o set cpf ele pode já receber uma instância de cpf ou poderia receber uma string e instanciar aqui dentro. Eu normalmente gosto de receber como String e aí fazer a validação aqui dentro. E justamente validar, aqui na real a gente não precisa validar porque o próprio cpf já valida se está nulo ou se é ou se não casa com isso aqui a mesma coisa para o sete e mail só que ele vai receber aqui uma string e new e mail e o sete e o set name é o name isso é o name que ele vai dar um new name e ele recebe como uma string aqui então dessa forma a gente por exemplo consegue até reutilizar aqui o dis.set name consegue até reutilizar aqui o 10.7 name e aí só faz essa instanciação essa validação e instanciação do próprio value object uma única vez então 10.7 e mail bacana e o customer A gente pode ter também, né? Vamos lá, vamos fazer aqui, ó Set Ah, o Customer na verdade não pode ter Porque ele justamente é Ele é final ali Ok Beleza Então, ó, dessa forma a gente encapsula o comportamento De instanciar o ValueObject E de validar dentro dos sets Ok Vamos fazer isso para os demais também, como por exemplo partner, aqui a mesma coisa, então set cnpj só que recebe uma string e new cnpj set email recebe uma string new email e set name recebe uma string new name beleza, e aqui a mesma coisa set name set cmpj set email beleza e o event o event é até bom, vocês vão ver o benefício aqui, justamente porque olha quantas validações que o event tem. new name, name, set date, string, final, e a gente encapsula isso aqui no setter, e até a verificação, agora aqui, sai daqui, vem para cá, então a gente reaproveita a verificação e reaproveita a conversão da string para localDate. O partnerId também tem a validação aqui. O totalSpots também tem a validação aqui. Integer. spots também tem a validação aqui, integer, está vendo como a gente vai distribuindo a complexidade do software de maneira tática através dos nossos queridos objetos de valor, das nossas queridas entidades. Todo esse comportamento passa a ser validado de maneira distribuída. Olha como fica bem mais conciso, mais claro aqui, certo? Ó, top. Agora a gente pode partir por ticket é vamos partir por ticket, mas não só o ticket, como também está faltando aqui no event o relacionamento que a gente não portou vamos lá no model event, esse relacionamento com o ticket. A gente pode fazer esse relacionamento existir sim. Na verdade, existem as duas abordagens, relacionamento direto ou relacionamento só com a contagem de maneira com consistência eventual. Como que a gente faz o relacionamento direto? Para fazer o relacionamento direto, a gente vai ter que criar aqui por exemplo um novo cara que seria event ticket o event ticket ele vai ter private final ticket ID que ainda não existe. Ele vai ter Private Final Event ID, o Event ID já existe ele vai ter private int ordering justamente para saber qual ordem ele é e acho que só ele vai relacionar o ticket para o evento beleza só agora a gente vai criar então aqui o ticket id não é para gente ticket id ticket id ticket id ticket id agora ele já tem ali beleza a gente vai criar o nosso construtor vamos lá insert constructor search constructor, constructor com todos eles, final beleza, final e final ordering, esse aqui a gente protege aqui, if, xigshed igual a nulo throw new validation exception, vamos lá só copiar aqui o validation exception beleza invalid ticket id for event ticket, a gente garante agora que ele não é nulo event ticket, a gente garante que ele não é nulo, a mesma coisa para o event-id, final if ordering envolve o anulo a gente vai validar through new invalide ordering for event ticket beleza ok agora a gente pode criar aqui o getter pra todos na verdade não tem problema e esse cara ele vai ser protected vai ser protected justamente porque só quem estiver aqui dentro do pacote do event deveria vê-lo, como a gente ainda não tem o pacote event, ainda não faz muito sentido isso aqui, mas vai fazer, prometo pra vocês beleza aí agora o que a gente faz? esse cara faz sentido estar mapeado aqui, private set event ticket, tickets e a gente vai inicializá-lo, a gente pode até criar um construtor vazio justamente que inicializa esse cara né então quer dizer vazio não pode ser ele tem que receber o event id então dis.eventid igual a eventid, essa validação a gente até pode colocar aqui ó e justamente dis.tickets igual a This event ID Beleza Ah, só ver uma coisa Porque eu acho que eu É, isso mesmo, isso aqui tudo tem que ser Privado Os métodos setters devem ser privados Porque senão ele Passa a ser Uma entidade anêmica A gente vai poder mudar Alter alterar o comportamento dele, setEmail, setName, setCpf, através de outros métodos que a gente, no caso, não tem implementado. Ou através de outros comandos, não através, chamando direto, os setters assim. Acho que eu tinha esquecido. Eu deixei tudo como público. Ele cria como público por padrão, mas tem que ser encapsulado beleza e partner, cadê o outro, o customer eu alterei o event beleza, vamos deixar aqui como tudo privado show de bola aqui encapsulamos show de bola já temos nossos tickets aqui agora a gente até pode criar o construtor não o getter para o ticket beleza então recebo o ticket só que aqui, collection unmodifiable set, a gente vai devolver uma cópia não modificável do nosso set para que ele manipule os tickets de fora do nosso domínio. Beleza. Acho que se a gente usar algo como alt tickets fica até um pouco mais claro beleza então vamos lá deixa eu fechar isso aqui já temos os nossos setters com as verificações já iniciamos os tickets igual a zero agora acho que estamos ok só contens are query but never updated. Claro, a gente ainda não está atualizando os tickets, mas a gente um passo de cada vez. Vamos lá. Estamos então aqui criando o ticket ID, agora a gente precisa criar de fato a nossa entidade ticket. Vamos lá. Ticket, lembra? Ticket se tornou uma raiz de agregação própria um agregado próprio na verdade que justamente pelo seu comportamento de poder ser atualizado independente do evento e de forma concorrente então explicando um pouco do cenário várias pessoas podem ir de forma concorrente. Explicando um pouco do cenário, várias pessoas podem ir comprar um ticket de um evento concorrentemente. Inaugurou agora o evento, abriu no nosso site, lançaram, eu e você podemos ir comprar um ticket, nada impede. Na hora de comprar o evento, ele precisa ter um mecanismo de proteção para a concorrência que normalmente é utilizado no Optimistic Locking que a gente vai ver numa aula específica de Optimistic Locking mas principalmente depois que a gente implementar a parte dos Driven Actors, um passinho de cada vez depois que a gente comprou o ticket, vai para o meio de pagamento isso tudo normalmente é feito de maneira assíncrona e o retorno do ticket, do pagamento também é feito de maneira assíncrona e pode atualizar múltiplos tickets de uma única vez agora imagina se eu tiver que carregar o evento para carregar todos os tickets toda vez que eu for atualizar um ticket, fica meio ruim, certo? então por isso que o ticket se tornou um membro privilegiado aqui e se tornou um agregado onde eu consigo manipular cada ticket independente um do outro independente do evento legal? então vamos lá private final ticket id ticket id aqui agora eu não lembro o que o ticket tem vamos dar uma olhada aqui é customer event e esses atributos então vamos lá private, é customer id private event id é e os atributos beleza vamos gerar um construtor aqui show de bola final, final, final, final, final, beleza Agora a gente pode gerar os getters aqui e os setters também, para facilitar aquele mesmo esquema que a gente já viu. A diferença é que para os setters eles vão ficar privados e vão encapsular as verificações. Então deixa eu ver como é que está aqui no event, por exemplo e aí eu copio dessa mesma forma pra lá então, ó customer id for ticket ticket, legal set event id beleza validado set status inválido status beleza status aqui set paid at nulo e reserved at nulo também o reserved at não pode ser nulo porque logo quando cria ele já seta esse cara então vamos colocar aqui uma verificação o paid at pode ser nulo sim porque a gente pode setar e depois a gente pode querer limpar, inclusive. Ticket ID. Ok. Então, estamos setando o ticket ID ali. Set customer. Event. Set status. Opa. Set paid. setStatus, opa, setPaid, setReserved, show de bola. Beleza, então temos aqui, agora, vamos lá, vamos ver como é que está o caso de uso, porque daí a gente vai conseguir ver com mais clareza o método que está faltando, porque está faltando o método aqui para instanciar o event ticket. E a gente consegue também já ajustar as coisas que faltam. Ah, antes de mais nada, a gente vai precisar do TicketRepository. Então já vamos criar. TicketRepository. Tudo que for event eu vou renomear para ticket. E aqui também, tudo que for event eu vou renomear para ticket. O ticket, se eu não me engano ele já tem mas vamos com calma vamos lá, subscribe customer to event, então a gente já consegue mudar aqui customer repository customer repository, deixa eu ver qual que ele pegou, pegou certo event repository e vamos também precisar do ticket repository, ticket repository. Beleza. Agora a gente coloca tudo aqui, coloca tudo aqui e esse cara a gente adiciona aqui como um terceiro parâmetro que ele ainda não existe vamos lá, customer repository, customer of id, vamos mudar aqui o input porque agora tudo string string e o resultado, antes não tinha o ticket, agora pode ter porque agora o ticket é um membro um membro independente, então pode ter também agora é customer id ticket id, não, customer id with e aí ele vai retornar um e aí a gente vai retornar um evento with, é só pra manter o padrão ali em cima, né, tá chamando an event e aí, aqui antes tinha uma busca find ticket by event and customer, certo? Só que isso não é mais necessário e essa validação também que tá aqui no caso de uso não vai ser mais necessária estar aqui. A gente vai conseguir validar esse cara dentro do nosso agregado. O agregado precisa manter a consistência de suas invariantes, certo? Então deixa eu comentar. E o que eu vou fazer aqui? An event reserve ticket para quem? Para o customer. Opa, para o customer. E ele vai retornar um final, vamos colocar aqui ticket ticket do Application ticket. Esse ticket aqui e o ticket status ó, vala vala também. E aí a gente pode justamente adicionar import qualificado, beleza, isso aqui tudo vala. Reserve ticket, create method, e esse como é público vamos levar lá pra cima como ele tem um comportamento a gente pode chamar o ticket... Ah, verdade, antes de mais nada a gente precisa implementar as verificações, né? Ticket pro evento e pro customer. Então aqui ó, a gente consegue verificar dis.alltickets.stream. filterit it. Se bem que a gente não tá armazenando de quem é o customer lá no event ticket. Pode ser interessante a gente armazenar esse cara justamente para manter essa invariante. Só que a gente tem que pensar que e se o ticket mudar de titularidade? Se mudar de titularidade, compl de titularidade complica não podemos fazer dessa forma se não mudar de titularidade poderíamos adicionar dentro de event ticket o customer id então vai depender de cada regra de negócio. Certo? O que a gente pode fazer? Vamos assumir que o ticket não pode mudar de titularidade? Ou pode? Bom, vamos assumir que um ticket não pode mudar de titularidade, né? Normalmente nos eventos você não compra e dá pra outra pessoa, né? Normalmente é... quer dizer, normalmente nem tem esse tipo de validação, né? De titularidade. Mas... normalmente não pode mesmo alterar a titularidade. Ah, e aqui eu até esqueci de chamar o setOrdering. A gente vai fazendo e vai pegando as coisas. Mas vamos fazer o seguinte, então. Event Ticket. Vamos colocar aqui o Customer ID. A gente adiciona esse cara, ticket ID, mais uma validação beleza e aqui diz .customerID customer ok e esse aqui é privado uma ideia cria com o público, a gente tem que sempre lembrar de olhar então beleza, agora a gente tem o customer aqui, então agora a gente consegue ver . Significa que a gente precisa lançar uma nova exception que é justamente essa aqui e-mail already registered então o cara já comprou show de bola a outra validação é o seguinte a gente consegue colocar aqui também se não se não nesse se passou a gente vai pegar o total spots e o ao tíquete ponto size plus one é mais um isso aqui a gente consegue sair para uma constante 1 que vai ficar aqui como private static final ele vai ficar como o link a requerir whenever o pedido claro a gente vai utilizar nos preocupa event sold out senão a gente vai retornar então um ticket ponto new ticket é cadê cadê a gente tem tudo isso aqui a gente pode criar então nosso factory method aqui ó pra facilitar né um ticket new ticket que recebe um customer id, que mais? Um event id e só, para criação só isso já é suficiente. Return new ticket e aí ticket id unique customer id event id ticket status pending page at nulo e instant .now para data de reserva. Aqui a gente vai criar um novo ticket com esse customer e com este ID de evento final var new ticket e aí a gente vai retornar new ticket mas antes disso a gente precisa adicionar disk.tickets.add newEventTicket e esse cara ele vai receber o id, o evento, o customer e a ordem então é basicamente o id do evento newTicket.id o customer o id do evento e a ordem que é basicamente o ticket.size plus one. O que ele está reclamando aqui? Ah, evento, depois o customer. Então, vamos mudar lá. Evento, depois o customer. Plus one. Show de bola. Então, olha como a gente começou a encapsular essas regras, essas invarianças, invariantes, dentro de métodos da nossa raiz de agregação. E aí isso aqui a gente não precisa, basicamente. O que a gente precisa fazer é criar o... Ah tá. criar o ah tá ticket repository ponto create ticket e event ponto update an event e aí a gente devolve o output aqui o output agora a gente mudou pra string string, beleza? Então a gente tem aqui an event .eventid a gente tem o ticket.ticketid ticket.status.name e ticket.reservedAt Ah, e esses caras, né, como são value objects status.name e ticket.reservedAt. Ah, e esses caras, como são value objects, perfeitamente. Bom, esse então é um caso de uso um pouco maior, um pouco mais denso, porque ele acaba envolvendo duas entidades. A primeira, que são dois agregados. A primeira é aquela do ticket e a segunda é aquela do event. Essa é a única forma de fazer isso? Não, não é. Existe uma maneira através de eventos de domínio, que eu vou ensinar para vocês essa outra maneira também. Como a gente pode persistir, como a gente pode persistir, como a gente pode fazer esse caso de uso sem manipular dois sem manipular dois agregados de uma única vez. Tá? O que garante também uma consistência eventual, mas, de certa forma, a gente não mantém esse acoplamento aqui dentro desse caso de uso, né? Podendo, dependendo de quando crescer, virar uma Big Boss Mud, certo? Esse caso de uso tende a ser Big Boss Mud. Então, por exemplo, se eu quiser enviar um e-mail para o customer com a informação de que foi comprado com sucesso. É mais um cara que a gente tem que colocar aqui, que a gente vai ver como pode ser feito de maneira mais efetiva na próxima aula, porque essa aqui já está ficando bem comprida. Então, vamos só agora, rapidamente, corrigir os testes aqui. Deixa eu ver, a gente criou o domain. Na próxima aula, a gente vai fazer um pen-fee no domain, criar testes unitários pra eles, né, porque a gente tá sem, mas vamos ver se os casos de uso estão funcionando ou quanta coisa quebrou aqui, né, porque a gente mudou alguns atributos então isso aqui que era tudo long agora virou tudo string os identificadores são tudo string os casos de uso a gente ainda não tem a implementação dos repositórios então por isso que vai ser null falta mais um vou até colocar o to do aqui em todos esses fix dependencies fix dependencies a gente vai corrigir essa parte desse lado depois vamos continuar rodando os testes que acho que vai quebrar várias coisas aqui ó esse cara é string vamos de build em build ó agora sim chegou no cadê chegou no subscribe customer, esse cara a gente tem que corrigir, então vamos lá, a gente precisa de um repositório customer in memory ticket repository, o ticket, a gente tem alguma verificação extra? acho que não, é só tickets, então vamos corrigir vamos mudar tudo aqui que é customer vai virar ticket é... tudo que é minúsculo aqui vira ticket também esses aqui tchau esses aqui tchau ticket.valid ah, esse toString redundante agora já não tem mais necessidade a gente só precisa de um índex como assim então tirar tudo aqui esse remove redonda beleza beleza acho que é só isso que a gente precisa vamos corrigir o teste aqui então, ó, deve comprar um ticket de um evento. Esse cara aqui é o mais complexo, porque ele tem, ele mexe com vários agregados. Então, o que a gente pode fazer aqui, ó? Ele mexe com evento, com partner e com customer, né? Então, vamos lá, vamos no event e vamos copiar esse partner, Vamos passar pra cá Beleza Agora a gente pode ir de novo no event E vamos Cadê? Não tem nenhum Event aqui? Ok, vamos copiar isso aqui Só pra facilitar Event .event Aqui .newEvent name date totalSpots e partner uma date, total spots vamos colocar aqui que tem 10 e o partner beleza só pra não ficar com esse import todo tosco, a gente já pode remover tudo que é infraestrutura daqui beleza, e aí esse cara a gente pode, qualify beleza final var and event evento já temos o partner e já temos o evento agora a gente pode fazer um costume que já precisar tem algum que já tem um customer pronto ok vamos trazer o customer a gente coloca este cpf aqui um nome como esse só que a gente pode colocar aqui pra Gabriel Do Final var A customer Beleza, a gente já tem um customer Então, ó, customer id A customer .id.valet Event id An event Event id.valet Isso aqui, tchau tudo isso aqui que é mock tchau a gente pode criar aqui em cima final var customer repository igual a new in memory customer repository final var event-repository igual a new in-memory-event-repository, finalvar ticket-repository igual a new in-memory-ticket-repository. Vamos passar para o subscribe esses todos agora a gente precisa criar então customer.create a customer event.create an event assert equals event output event not null reservation not null para o ticket e pend para esse cara, legal e esse aqui event id beleza expected ticket size expected ticket size a gente consegue verificar aqui event repository ponto event of id event id an event ponto event id final var actual event e aí assert equals expected ticket size actual event ponto all .get ponto all tickets ponto size perfeito agora vou copiar isso aqui tudo que a gente vai precisar nos demais igual tickets.size Perfeito. Agora, eu vou copiar isso aqui tudo que a gente vai precisar nos demais. Então, não deve... Vamos lá, estamos acabando. Não deve comprar um ticket com um cliente não existente. Então, isso aqui tudo vai para a vala. Aí a diferença é que a gente não vai persistir o cliente. A gente até pode instanciar, mas só pra não ficar confuso não vou instanciar um novo cliente eu só vou customer id como se tivesse inventado um id de customer aqui não deve comprar um ticket de um evento que não existe então vamos lá colar agora o evento é que não existe então a gente nem precisa desses dois e aí esse aqui a gente consegue utilizar o event id com o unique direto o mesmo cliente não pode comprar mais de um ticket por evento tá bom colar então persistiu customer persistir o evento aí eu vou fazer o seguinte então ó esse cara como reserve ticket passando o customer.id e aí ele retorna um ticket claro então colocar para uma variável colocar para uma variável e aí aqui a gente tem que persistir an event e aí a gente tem que persistir o ticket repository de um evento que não existe mais cadeiras tá bom é vamos copiar novamente é isso aqui tudo que é o que deu certo ea gente modifica depois estão aqui tudo vai provar então mesmo cliente não pode comprar de um evento que não há mais cadeiras vamos mudar todas as fotos pra um aí ó eventos vão fazer o seguinte mesmo mesmo treta eventos reserve ticket para um customer. Final var ticket. É aqui, ticket repository.create ticket. Então, ou seja, a gente já reservou um ticket aqui. Se eu passar um Customer2, Pedro dou, Pedro dou, e aqui um 1. Na verdade, eu vou persistir o Customer2 e o Customer1. Só que quem comprou o ticket foi o Customer2. Aí o Customer1 vai tentar comprar o ticket depois. E ele não vai conseguir. Então vamos rodar os testes. Deixa o string. Ainda tem alguns strings quebrando. Aí, perfeito. Todos os testes passaram. E aí eu fico com a pergunta, né? Você confia num refactor como esse se você tivesse testes que não são efetivos? Testes dos quais você não confia? Fica complicado, mas a gente verificou aqui todos os testes, verificamos as propriedades, verificamos todas as validações, verificamos tudo bonitamente. Agora a gente pode organizar o import. Verificamos todas as validações, verificamos tudo bonitamente. Agora a gente pode organizar o auto-import. Vir aqui, organizar o auto-import de tudo, identar tudo. Tudo funcionando bonitamente. Os testes integrados, obviamente, não estão funcionando ainda, porque a gente ainda não corrigiu essa parte. E pode ser que alguns outros testes da aplicação também não estejam funcionando, porque a gente ainda não colocou as dependências necessárias. O que a gente vai fazer agora, na hora de corrigir o lado dos secondary actors. Combinado? Então é isso, espero que vocês tenham gostado, vejo vocês na próxima aula.