Тёмный

294 - Concorrência na CLEAN ARCHITECTURE | theWiseDev NFR 

Otavio Lemos
Подписаться 36 тыс.
Просмотров 2,8 тыс.
50% 1

Muita gente fica com dúvida em como implementar alguns tipos de requisitos não funcionais como controle de concorrência em uma Arquitetura Limpa.
Neste vídeo te mostro uma implementação de lock otimista em uma aplicação de aluguel de bikes escrita em Node.JS/TypeScript com o ORM Prisma.
Em particular a implementação evita aluguéis ou reservas concorrentes da mesma bike para datas com intersecção.
Chega mais para ver como ficou massa...

Наука

Опубликовано:

 

15 сен 2024

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 47   
@fabiosousadesantana3861
@fabiosousadesantana3861 8 месяцев назад
Ótima abordagem sobre o tema, uma das melhores que eu vi.
@phx.rafael
@phx.rafael Год назад
Comentando pra aumentar o alcance.
@RenanCarneiroOliveira
@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
@otaviolemos Год назад
Legal, valeu!
@joaomarcos.85
@joaomarcos.85 Год назад
Excelente vídeo! Seria legal uma abordagem usando transações também!
@igorlmfs
@igorlmfs Год назад
Acho que talvez uma abordagem com unit of work ajuda nesses casos
@DeyvsonAguiar
@DeyvsonAguiar Год назад
top!
@JoaoVictor-ge8bx
@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
@EmanuelMassaferaMagalhaes Год назад
Comentando para acompanhar a discussão...
@joaogabrielmp
@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
@igorlmfs Год назад
Tenta dar uma olhada no padrão UnitOfWork, acho que é uma maneira interessante de resolver esses problemas 😊
@Scantrex
@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(); }
@tiagokjjz
@tiagokjjz Год назад
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
@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
@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.
@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
@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
@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
@otaviolemos Год назад
Eu acho que fila nesse caso é bem overengineering... :)
@lucaspereiramadeira
@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
@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
@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).
@joaooliveira900
@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
@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
@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
@otaviolemos Год назад
@@joaooliveira900 isso!
@LeandroRamos086
@LeandroRamos086 Год назад
Mas se for outro candidato o erro vai passar? (desculpa o pitaco e sou meio leigo, pergunta para fins educacionais)
@otaviolemos
@otaviolemos Год назад
Acho que tava com esse errinho mesmo, mudei o unique no schema para @@unique([bikeId, candidateId, optimisticCounter])
@joaomarcos.85
@joaomarcos.85 Год назад
O repo do código do vídeo é publico? Poderia compartilhar o link?
@otaviolemos
@otaviolemos Год назад
Infelizmente não. Apesar de eu ter alterado um pouco, o original é usado ainda no processo seletivo na Trio.
@GustavoHenrique-xg4ey
@GustavoHenrique-xg4ey Год назад
link do repo?
@otaviolemos
@otaviolemos Год назад
Não posso compartilhar, Gustavo, porque está sendo usado nos processos seletivos... :)
@geisonflores
@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
@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
@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
@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
@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
@otaviolemos Год назад
@@geisonflores boa ideia: agora entendi! Vou considerar e, caso ficar bom, faço uma errata.
@artu_almeida
@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
@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
@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
@artu_almeida Год назад
perdi a parte do processo seletivo kkkkk
@otaviolemos
@otaviolemos Год назад
@@artu_almeidaalém disso, não é só um CRUD; a parte de aluguel é mais complexa. Não é só cadastro.
Далее
😂😂
00:16
Просмотров 869 тыс.
Node.js is a serious thing now… (2023)
8:18
Просмотров 646 тыс.
Apple October Event LEAKS - 7 NEW Devices are COMING!
10:20
293 - JAVASCRIPT Imutável! | theWiseDev Functional
15:08