SOLID DIP Dependency Inversion Principle + Design Patterns Adapter, Abstract Factory e Singleton
HTML-код
- Опубликовано: 5 фев 2025
- SOLID DIP Dependency Inversion Principle + Design Patterns Adapter, Abstract Factory e Singleton
Saiba mais sobre o curso de Clean Code e Clean Architecture em branas.io/curs... - Наука
Comprei o seu curso depois de assistir essa aula, esse passo a passo de construir cada camada somente quando necessário é uma didática que nunca tinha visto. Dá pra absorver muito mais assim. Parabéns.
Se tivesse outro botão de like, clicaria de novo. Que aula! Fera demais!
Poxa vida, é uma delícia assistir os vídeos que você publica, assisto a todos inclusive os shorts! Valeu mestre!
Sempre compartilhando conhecimento.
Continue assim Branas.
Obrigado.
Super claro como sempre :). Me preparando para entrar na próxima turma
Nunca me canso de aprender aqui nesse canal, Branas pode falar melhor sobre como tratar os errors que podemos gerar nas validações de parâmetros dentro do modelo, lançar Error parece não ser a melhor opção, como posso tratar isso sem quebrar a aplicação retornando apenas notificações simples ou acumuladas de todas as props.
O melhor! Parabens mestre.
Poderia expor um contexto de um sistema legado, e aplicar o que der....vejo que seria um pouco mais complexo inserir essas ideias em um legado....seus conteúdos são bem legais, parabéns🙂
Concordo, a maioria esmagadora das atividades envolvem cuidar de sistemas legados!
Bom demais ver um video desses!
muito grato ao Branas, pelas aulas de angular 1
evolui bastante como programador naquela epoca
agora com as aulas de clean architecture nem se fala
Muito, muito bom mesmo.
Olá Rodrigo, você poderia trazer um conteúdo de clean arch com autenticação de usuários ?
Vim pelo o Instagram. É like.
Branas, eu fui membro da turma 7 ou 8 do teu curso (não me recordo exatamente qual delas), e fiquei maravilhado com tudo o que aprendi lá. Porém, ao final eu fiquei com várias dúvidas porque senti que o projeto final ainda não atendia a todos os requisitos "básicos" de uma aplicação. Vou dar um exemplo:
Eu vejo como a criação de um Domain Model e Value Objects são interessantes e "necessárias". Uma aplicação (que sempre é o exemplo que todo mundo dá) é a de validar os dados de entrada, como você mesmo fez com o email e com o preço do ingresso. No entanto, pensando que normalmente criamos APIs, e que esses erros vão para o cliente que fez a requisição, como é que esse tipo de validação realmente faz sentido, sendo que eu poderia validar todos os meus dados de input antes mesmo de iniciar a execução do meu use case? Ou até mesmo ser a primeira coisa que meu use case faz (validar os dados de input), e assim eu sei que eu tenho as regras mínimas para executar esse use case corretamente.
Normalmente é isso que eu vejo os frameworks, como NestJS, Laravel e afins fazendo. Na própria criação do DTO eu consigo criar as validações dos meus parametros, e isso parece muito consistente.
Apesar de eu ter entendido todos os conceitos que você apresentou, ainda ficaram essas lacunas, para que eu possa, por exemplo, pegar um projeto NestJS e desacoplar ele do dominio e afins, com um tratamento de erro adequado, sem ter que fazer camadas e camadas a mais do que os frameworks já sugerem.
Eu peguei todos os ensinamentos do seu curso e apliquei em um projeto freelancer que fiz, e foi razoavelmente bem, mas não utilizei muito a camada de dominio porque não era um dominio complexo, e mesmo que fosse, eu não consegui entender o valor além da "validação dos dados".
Eu sei que Domain Driven Design é feito para projetos com dominios complexos, mas abordagens onde utilizam ao menos os conceitos do DDD e os padrões que você ensina deveriam funcionar bem nesses casos, mas ainda assim é muito dificil de aplicar.
Eu posso compartilhar com você o código que criei para esse projeto, se você tiver interessado em olhar.
Uma das principais vantagens de ter uma camada de domínio rica em comportamento e não "anêmica" é a possibilidade de testar e validar o domínio de forma isolada. Em domínios pequenos, realmente, o DDD pode não fazer sentido, pois seu propósito é "atacar a complexidade no coração do software". Aqui, complexidade se refere exclusivamente ao domínio, ou seja, às regras de negócio, e não à complexidade técnica.
Por isso, as validações de negócio devem estar dentro da camada de domínio, tornando-a "autossuficiente" e facilitando o desacoplamento. Isso não impede que haja validações em outras camadas, mesmo que pareçam duplicações num primeiro momento". No seu exemplo, você poderia validar o input HTTP na camada do framework também, mas isso não exime a responsabilidade do domínio em validar a integridade das regras. Ou seja, validações em uma ou mais camadas! Você pode até não colocar validações em outras, mas validar o domínio... é algo mais do que obrigatorio eu diria! Antes o seu cliente da API receber um 500 "Server error" referente a uma validação de email inválido que foi propagada da camada de domínio e não tratada na camada HTTP por exemplo, do que não receber erro nenhum :)
Imagine um cenário onde você decide trocar de framework. Se o seu domínio está completamente isolado, teoricamente, é mais fácil integrá-lo ao novo framework. Mesmo que alguma validação no framework seja esquecida ou alterada durante a migração, o domínio atua como a "última barreira" de integridade, evitando que o estado da sua aplicação fique inconsistente.
Em resumo, é difícil perceber a riqueza que um domínio bem modelado traz em domínios pequenos, mas posso afirmar que faz total diferença em domínios complexos.
Ter um domínio rico e alinhado com a linguagem ubiquoa arrisco dizer que é o "estado da arte" da modelagem de design de software, o domínio deve "gritar" o seu propósito rs
Se a sua aplicação tem uma natureza mais de CRUD, talvez seja mais simples usar outras abordagens.
E lembrando que DDD não é só tático, antes mesmo de pensar nesse tipo de problema, vale fazer o DDD estratégico.
Se você quiser, vou deixar o exemplo do Vaughn Vernon usado no livro sobre DDD, implementado neste repositório do GitHub github.com/VaughnVernon/IDDD_Samples
Espero ter conseguido ajudar com a sua dúvida :)
Qual seria a diferença entre usar o Abstract Factory e o Composite pra montar as depedencias de um objeto?
Top demais!!!
Branass, eu tenho um agregate e neste cenário preciso persistir em duas tabelas, posso ter mais de um método save no repository do agregate?
Ótimo! Entendi que o uso do Abstract Factory foi para fins didáticos, mas gostaria de fazer um comentário. Nesse exemplo, ele viola o Interface Segregation Principle, pois obriga o cliente a implementar dois métodos de criação, mesmo que ele precise usar apenas um deles. Além disso, o componente de baixo nível passa a ter conhecimento de métodos desnecessários dentro da factory. É um detalhe pequeno, não quero parecer purista rs, mas achei relevante comentar também para fins didaticos.
Adorei a reflexão… faz sentido sim… de certa forma o ISP acaba sendo pouco seguido, mas seria possível realmente o use case segregar uma interface para que o abstract factory implemente, desacoplando do contrato da factory… boa, valeu!
Olá Felipe (também me chamo Felipe).
Enviei um comentário para o Branas mas talvez você também possa ter essa resposta (considerando o nível de explicação que deu ao colega mais abaixo e também pela sua excelente colocação quanto ao uso do Abstract Factory).
Resolvi repeti aqui pois sei que comentários diretos ao usuário são notificados por e-mail.
Então aqui vai... (novamente)
Fiquei com uma dúvida quanto ao uso dos repositories.
Considerando que o retorno dos métodos de um repositório é uma entidade do domínio, como ficaria para dados que serão paginados?
Um exemplo: caso meu useCase esteja esperando um objeto contendo o número de registros, numero de páginas para entregar ao frontend (que cuidará de paginar os registros visualmente) e os dados de cada um dos objetos em um array, isso iria ferir o conceito do repository.
Olá @@opcoesdenegocio7948
No DDD, o conceito de repositório deve ser aplicado a um agregado, e não a uma entidade isolada. Um agregado é responsável por garantir as invariantes de negócio (regras que devem ser sempre satisfeitas) e definir um limite transacional no conjunto de objetos que ele engloba.
No "command side" (utilizando a terminologia CQRS), o repositório é essencial para aplicar mutações de estado no agregado. Para isso, é necessário carregar todo o estado do agregado em memória, incluindo todos os value objects e/ou entidades que fazem parte do seu limite.
Por outro lado, no "query side" (ou consultas), não é estritamente necessário usar um repositório. Alternativas como DAO (Data Access Object) ou QueryObject podem ser aplicadas, permitindo acesso mais eficiente aos dados.
Em resumo, no "command side", um repositório deve conter métodos simples como "FindByID" ou "Save", já que a intenção é realizar uma mutação de estado no agregado e garantir as invariantes de negócio. Já no "query side", podem ser utilizadas outras estruturas de dados e formas mais eficientes de acessar o banco de dados, sem a necessidade de carregar todo o estado do agregado. Métodos específicos de paginação podem ser implementados para devolver estruturas de dados mais simples que atendam às necessidades específicas do cliente (ou frontend).
Pensando puramente em consultas, é mais eficiente usar uma query direta que traga o resultado em objetos simples, do que carregar todo o "overhead" de um agregado inteiro apenas para transformá-lo em um objeto simples para devolver ao front (uma vez que se trata apenas de uma consulta, não haveria mutação).
O coração do seu software está no "command side"!
Espero que isso ajude com a dúvida! Se precisar de mais detalhes, estou à disposição.
@feliper.alcantara3024 muito obrigado pela resposta. já havia chegado a uma conclusão aproximada da necessidade do uso do DAO ao invés de um repository e sua excelente resposta confirmou. no último projeto que trabalhei, o arquiteto solicitava esse tipo de estrutura para o time de BE, onde uma estrutura bastante complexa era carregada em um array dentro de um objeto com um data interno, passando o restante das informações de paginação na camada mais externa desse objeto.
Ex (aproximado):
{
page: 1,
perPage: 10,
lastPage: 7,
data: [{ ... }]
}
com a explicação do Branas e conversando com um amigo que é da turma 12, ele disse que mesmo no projeto desenvolvido no curso ele não chegou a abordar uma estrutura parecida com essa (algo paginado) mas que também ninguém perguntou do tipo nas sessões de Q&A durante a aula.
estou me preparando para entrar na próxima turma e espero ver algo com consultas um pouco mais complexas onde há relacionamento entre tabelas e montagem de payloads "estranhos" como este do exemplo para servir a um cliente.
de qualquer forma, muito obrigado pela sua excelente e detalhada resposta.
é um prazer ter esss espaço de conversa com alguém que, visivelmente, tem bastante conhecimento e segurança no que fala.
abraços
@@opcoesdenegocio7948 é isso! E no fim dia tudo se resume em abstrações… DAO é só mais um nome bonito pra algo que somente visa segregar responsabilidade, não seria um grande “pecado” também termos um ReadRepository ou QueryRepository separado apenas para consultas… O mais importante são os conceitos, a implementação é detalhe! Fico feliz em saber que pude contribuir, qualquer coisas estou por aí :)
Fiquei com uma dúvida quanto ao uso dos repositories.
Considerando que o retorno dos métodos de um repositório é uma entidade do domínio, como ficaria para dados que serão paginados?
Um exemplo: caso meu useCase esteja esperando um objeto contendo o número de registros, numero de páginas para entregar ao frontend (que cuidará de paginar os registros visualmente) e os dados de cada um dos objetos em um array, isso iria ferir o conceito do repository.
Você não está ferindo o conceito de repositório adicionando paginação, porque Repository é um pattern para criar uma camada entre a comunicação do banco de dados e a aplicação, e você pode decidir no seu design se seu repository vai ter funcionalidades de paginação, como, e também como separar as responsabilidades.
O vídeo trata sobre como usar o DIP para decidir como desacoplar um código cliente de um código fornecedor (como exemplificado, o usecase e o repository). Naturalmente, poderíamos chamar um repository direto no usecase, mas isso cria uma dependência no usecase do repository diretamente, que é um módulo de mais baixo nível (low level module). Com o uso de algumas técnicas para desacoplar e inverter essa dependência do módulo de alto nível do módulo de baixo nível - como pelo uso de interfaces - uma coisa que você pode fazer é dizer na interface que todos os códigos clientes (por exemplo, seu usecase) poderão receber objetos de paginação.
Assim você declara QUAIS objetos serão necessários para sua funcionalidade de paginação (implementação abstrata), mas em outro momento, você ou outro desenvolvedor pode implementar COMO a paginação será realizada (implementação concreta). E lembrando que Interfaces são uma das técnicas do conjunto de técnicas mostradas no vídeo para deixar o projeto com código bem desacoplado e com alta coesão. É preciso praticar e pensar nas consequências no uso de cada uma dessas técnicas, e essas consequências podem ajudar (ou atrapalhar) a implementação da sua feature de paginação.
Espero que tenha ajudado. Tbm to aprendendo mt com os vídeos do Branas, cara é brabo!
@@LaurenceZanotti Obrigado pela resposta. o @feliper.alcantara3024 também me respondeu um pouco mais abaixo pois enviei a mesma pergunta dentro de um comentário dele para que o youtube notificasse por e-mail.
O uso de DAO passa a fazer sentido nesse caso pois o retorno do Repository (pelo que entendi até então) obrigatoriamente está fortemente ligado à Entidade (ou agregados) enquanto que o DAO me permite maior flexibilidade de construção de retornos mais "exóticos".
O ponto é: implementar uma interface para o output dos métodos no DAO e exportá-la para expor ao useCase considerando que o useCase não terá mais uma referência forte, como quando usamos uma entidade, faz sentido nesse caso?
@@opcoesdenegocio7948 Acabei de verificar comentários de vocês abaixo, muito boa as considerações. Apenas considerei o DIP e não tinha discernido a questão do objeto de entidade e sua relação com o repository. Muito interessante também os comentários dos outros patterns comentados, como os agregados, e as considerações do uso do DAO.
Até onde vejo, acredito que a interface ajudaria apenas a evitar um acoplamento de conteúdo. Nesse caso, como há uma necessidade específica de estruturação no nível dos dados, a mudança da estrutura deles seria mais fácil de manejar. (por exemplo, como o Branas mostra, se o nome de uma variável mudar no banco, é possível adaptar na implementação concreta, evitando mudanças em outros módulos como no usecase). Nesse caso vejo fazer sentido. No momento não vejo outras considerações para o exemplo que foi dado sobre o `{..., data: [...]}`.
Muito interessante o questionamento. Decisões de design e a prática dessas tomadas de decisões são muito cativantes. Obrigado por compartilhar
O pessoal aqui da empresa só fica imaginando como seria um code review do Branas.....kkkkk o medo se instaurou!
Branas eu VOU FAZER O CURSO kkk mas no momento preciso terminar a faculdade... porém minha dúvida é: ele é tipo... Clean arc e etc.. for dummies? Or beginners? Já que é na pegada de mentoria fico pensando que posso não interagir tanto por falta de conteúdo.