Ótima abordagem sobre o tema, uma das melhores que eu vi.
@phx.rafael Жыл бұрын
Comentando pra aumentar o alcance.
@joaomarcos.85 Жыл бұрын
Excelente vídeo! Seria legal uma abordagem usando transações também!
@igorlmfs Жыл бұрын
Acho que talvez uma abordagem com unit of work ajuda nesses casos
@RenanCarneiroOliveira Жыл бұрын
Ótimo vídeo, Otávio! Não sei se já viu, no caso de usar um banco Postgres, é possível lidar com isso pelo do banco de dados, utilizando exclusion constraints com tsrange. Ex: execute "CREATE EXTENSION IF NOT EXISTS btree_gist;" execute """ ALTER TABLE appointments ADD CONSTRAINT overlapping_appointments EXCLUDE USING GIST ( doctor_id WITH =, tsrange("from", "until", '[)') WITH && ) WHERE (NOT canceled); """
@otaviolemos Жыл бұрын
Legal, valeu!
@JoaoVictor-ge8bx Жыл бұрын
Ótimo conteúdo! Estou usando o Prisma no meu trabalho, e surgiu um pequeno gargalo quando tivemos que implementar uma transaction no banco quando utilizando o padrão Repository. Consegui resolver o problema criando uma solução que não gostei muito, e pesquisando na internet vi que outras pessoas tiveram esse problema. Vc tem algum exemplo de como vc lida com esse cenário em que vc precisa chamar, o método create() por example, de vários Repositorys diferente, dentro de um UseCase, tratando tudo como uma única transação no banco?
@EmanuelMassaferaMagalhaes Жыл бұрын
Comentando para acompanhar a discussão...
@joaogabrielmp Жыл бұрын
Ao meu ver, tem duas formas de resolver. A primeira seria no seu Controller ter a invocação da Transaction e ser repassada para o UseCase como uma dependência. Neste caso, pode realizar algo mais elegante para ficar junto ao acesso ao banco, algo como o UseCase espera um conjunto de repositories e junto vai a transaction. A segunda forma seria ter na camada de acesso aos dados, um repository agrupado e nele trataria a transaction, e ao repassar para UseCase, passar esse agrupado ao invés de dois métodos isolados.
@igorlmfs Жыл бұрын
Tenta dar uma olhada no padrão UnitOfWork, acho que é uma maneira interessante de resolver esses problemas 😊
@Scantrex Жыл бұрын
Imagino que você esteja tendo essa dificuldade por um problema de design. O pattern repositório, apresentado no DDD, é pensado para abstrair a persistência de um agregado (conjunto de objetos) e não apenas uma entidade. Uma transação que cruza essa barreira e lida com objetos de diferentes agregados é um anti pattern. Dito isto, uma possível solução para resolver esse problema no seu atual design seria (pseudocódigo): ts = db.newTransaction(); try { objeto1Repo.createWithTransaction(ts, objeto1); objeto2Repo.createWithTransaction(ts, objeto2); ... ts.commit(); } catch (err) { ts.rollback(); }
@diegokz Жыл бұрын
Otávio, se me permite, gostaria de levantar um ponto. A lógica do optimistic counter é efetiva quando surge uma operação concorrente de reserva. Porém, caso surja, neste intervalo entre a checagem e a reserva, uma operação de des-reserva, seguida de outra de reserva, haverá o mesmo valor no optimistic counter, sendo que as datas mudaram no banco. Logo, haverá duas reservas em períodos de intersecção concorrentes.
@otaviolemos Жыл бұрын
Diego, acho que entendi. Nesse caso parece que pode haver inconsistência. Mas é um edge case muito forte, vc ñ acha? Além de tudo a reserva removida tem que ser exatamente a última daquela bike (se for outra ñ haverá problema porque vai ter clash no counter). Se for um ambiente com muitos usuários concorrentes, de fato, o melhor talvez seja meter um lock nos dados.
@otaviolemos Жыл бұрын
Pensei em uma solução: só fazer soft-delete das reservas. Assim nunca perderemos o contador otimista e quando checamos por disponibilidade só consideramos as reservas ativas.
@DeyvsonAguiar Жыл бұрын
top!
@carlosvaltersantosferreira2217 Жыл бұрын
Quando eu programava em PHP usando Zend Framework, tem um recurso de controle de versão. Tem muito tempo isso, mas deixa eu tentar lembrar. Cria um campo Integer chamado version na tabela, e quando vc consulta trás a versão. Quando for mandar salvar no caso a locação coloca como condição da query a where version= ao valor que vc consultou, e ao mesmo tempo vc atualiza o campo version em +1. Com isso caso alguém tenha alugado antes de vc, o número da versão já tera mudado e a condição where será falsa. Acho que o nome dessa técnica é controle de versão. Exemplo: • Consultei a bike disponível, retornou version = 10 •Vou alugar, então na query ficaria update bla bla where version = 10 •Outra pessoa alugou antes de mim, o version passou a ser 11 •Quando eu tentar salvar como meu where version=10 será false, não irei conseguir alugar a bike Acho q é isso. Ah esqueci de falar, eu não precisava +1 no version, o próprio Zend fazia isso toda vez q tinha um update da tabela.
@otaviolemos Жыл бұрын
Sim, isso dá pra fazer com repeatable read configurado no banco e version na tabela.
@carlosvaltersantosferreira2217 Жыл бұрын
@@otaviolemos não sabia que isso era uma configuração de DB. Legal
@danroxha Жыл бұрын
Bom vídeo. Pensei em uma solução(async) com fila, mas o "problema" fica que o cliente não receberá d imediato se a reserva foi ou não aceita, contudo isso depende da regra d negocio, caso o negócio possibilite a locação opcional (2 opção de bike por exemplo) reduziria um pouco as reservas negadas.
@otaviolemos Жыл бұрын
Eu acho que fila nesse caso é bem overengineering... :)
@lucaspereiramadeira Жыл бұрын
E no caso de um cenário de comunicação com um checkout de pagamento para confirmar a reserva somente mediante pagamento qual seria sua solução Otavio? confirma o lancamento na base, faz comunicação com o checkout caso der erro faz o rollback? minha duvida é como ficaria essa solução em clean archtecture, essa comunicação com o mundo externo é um detalhe tambem?
@codelucas1224 Жыл бұрын
Talvez seja porque eu não vi todo o código mas tive a sensação de que você não criou as entidades que representam seu domínio, as regras de negócio e validação parecem “espalhadas” nos casos de uso e repository, tem algum motivo pra ter escolhido esta abordagem?
@otaviolemos Жыл бұрын
Estou querendo falar sobre isso num próximo vídeo. É uma versão simplificada da Clean Architecture quando o domínio é mais simples (maioria dos casos).
@LeandroRamos086 Жыл бұрын
Mas se for outro candidato o erro vai passar? (desculpa o pitaco e sou meio leigo, pergunta para fins educacionais)
@otaviolemos Жыл бұрын
Acho que tava com esse errinho mesmo, mudei o unique no schema para @@unique([bikeId, candidateId, optimisticCounter])
@joaomarcos.85 Жыл бұрын
O repo do código do vídeo é publico? Poderia compartilhar o link?
@otaviolemos Жыл бұрын
Infelizmente não. Apesar de eu ter alterado um pouco, o original é usado ainda no processo seletivo na Trio.
@joaooliveira900 Жыл бұрын
xô ver se eu entendi, eu poderia implementar uma logica simples tipo, numeroDoidao = soma do dia+mes+ano ai eu inseriria toda as infos + esse numeroDoidao no banco, porém, o campo estaria unico obviamente... sendo assim, tudo se resolveria porque o próprio banco daria erro por duas inserções estarem com mesmo número né
@otaviolemos Жыл бұрын
Mais ou menos isso. O unique restringe o id da bike e esse contador. O contador vai crescendo conforme o número de reservas da bike. Antes de fazer uma reserva eu pego o último contador; quando for salvar a reserva eu incremento esse contador. Se outro usuário fizer a mesma coisa, o contador + id da bike vão quebrar o unique, e ñ será possível realizar a reserva. Ficou claro? :)
@joaooliveira900 Жыл бұрын
@@otaviolemos Entendi, então na verdade não precisa fazer essa lógica maluca que eu fiz haha, só usar um contador e incrementar ele a nível de código mesmo, né?
@otaviolemos Жыл бұрын
@@joaooliveira900 isso!
@GustavoHenrique-xg4ey Жыл бұрын
link do repo?
@otaviolemos Жыл бұрын
Não posso compartilhar, Gustavo, porque está sendo usado nos processos seletivos... :)
@geisonflores Жыл бұрын
Tudo é tradeoff mas levar pro ORM essa regra causa o mesmo dano de deixar essa lógica em procedure no banco, se não documentar essa decisão e ter teste E2E pra esse caso de uso, vai ocorrer que no futuro o time muda, o ORM é trocado, e com isso alguém vai ter que resolver um bug em produção. Se a lógica de validação estivesse no seu domínio apenas testes unitários garantiriam a funcionalidade.
@otaviolemos Жыл бұрын
É só deixar nos dois; mas precisa fazer essa verificação no repositório para controlar a concorrência. Sugere alguma outra implementação? Isso que você falou não resolve a concorrência.
@geisonflores Жыл бұрын
@@otaviolemos Sim de fato vai precisar do repositório pois apenas ele vai poder fazer a consultas ao banco, mas a regra transacional eu acharia melhor ter no caso de uso, de modo que se fosse trocada a base de dados por uma estrutura de dados em memória o use case manteria a garantia de na hora de criar um "agendamento" todas as regras necessárias para ele ser feito rodassem igual.
@otaviolemos Жыл бұрын
@@geisonflores sim, eu implementei isso no meu fake object em memória também; ou seja, consigo testar o caso de uso sem o banco.
@geisonflores Жыл бұрын
@@otaviolemos entendi mas minha sugestão seria deixar publico o método do repositório "mostRecentBookingForBike" e levar a lógica da linha 85 a 94 do método rent do repositório para o método perform do use case, deste modo separaria bem os "concerns". O que acha, vc ve algum tradeoff nessa abordagem?
@otaviolemos Жыл бұрын
@@geisonflores boa ideia: agora entendi! Vou considerar e, caso ficar bom, faço uma errata.
@artu_almeida Жыл бұрын
porque a escolha de utilizar arquitetura limpa pra fazer um CRUD de aluguel de bikes? não vi no vídeo algo que justificasse seu uso, arquitetura limpa é pra sistemas com milhares de linhas de código... acho que uma arquitetura de 3 camadas padrão já resolvia sua vida😢
@TheSamfelgar Жыл бұрын
No caso do vídeo, especificamente, o projeto foi criado pra processo seletivo, então imagino q o objetivo seja só ver se o candidato sabe trabalhar com a arquitetura. Pessoalmente, eu avalio caso a caso, o nível de incerteza/complexidade da feature. Se for alto, uso algum padrão arquitetural mais complexo.
@artu_almeida Жыл бұрын
nossa viajei entao, eu vi ele falando q fez esse projeto pra empresa, achei que ia pra PROD e tals... demanda mesmo...
@artu_almeida Жыл бұрын
perdi a parte do processo seletivo kkkkk
@otaviolemos Жыл бұрын
@@artu_almeidaalém disso, não é só um CRUD; a parte de aluguel é mais complexa. Não é só cadastro.