Open zabaala opened 7 years ago
Isso é meio discutível, eu geralmente uso serviços para operações mais complexas do que simplesmente uma query
Vou trocar os métodos pra ficar mais discutível. Simplesmente buscar usuário não ta chegando onde eu quero =D
Pronto!!
Basta defini-las ou ler suas definicoes mais usuais
https://martinfowler.com/eaaCatalog/serviceLayer.html
Defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation.
A Service Layer defines an application's boundary [Cockburn PloP] and its set of available operations from the perspective of interfacing client layers. It encapsulates the application's business logic, controlling transactions and coor-dinating responses in the implementation of its operations.
https://martinfowler.com/eaaCatalog/repository.html
Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes.
Services atuam com operacoes mais complexas, repository trabalham como colecoes, de forma mais complexas tambem.
@IgorDePaula perdão pela liberdade, editei sua resposta pra facilitar a leitura da citação, tá?
Agora respondendo a mesma, compartilho desse pensamento também. Não sei se você chegou a ver, mas acabei de editar também a pergunta, para fazer referência a um caso mais complexo.
@zabaala agradeço pela edição. Relerei a pergunta.
Como dito na minha resposta primeira, o repository estaria mais para o lado de collections, ou seja, lista...nesse seu caso citado, estaria voltado mais para services, veja que alem de uma inserção, ele tem validacões e etc, este service pode utilizar repostories, via DI, aí ja é outra questão.
Antes de começar a responder creio que seja relevante colocar aqui as respostas do grupo do Facebook que foram muito pertinentes.
Se vc colocar um acesso ao banco de dados no seus services, isso iria causar uma dependência do service pelo banco de dados que é exatamente o que o repository evitaria. Assim seu service estaria dependendo do seu repositório, porém não do seu banco de dados (Que pode ser algo que venha a mudar no futuro)
O Marcelo Rodrigues respondeu:
É preciso separar bem o conceito de "Service" enquanto camada de negócio, a famosa Service Layer, do conceito de "Service", como instância de uma classe no container de dependência.
No DDD existem os padrões Service e Repository. O Service do DDD é levemente diferente do Service Layer. No Service Layer cria-se uma "fachada", usando o padrão Facade (importante não confundir com as Facades do Laravel), isolando e impedindo acesso direto as regras de negócio. Não é necessário saber como ele implementa regras e acesso a dados (por isso o nome Fachada). Numa situação prática, controller ou qualquer outra camada que precisar resolver qualquer coisa de negócio, conversa com a interface da Service Layer. A chance de ter um serviço inchado e com responsabilidades além da conta é alta com esse padrão, se não bem implementado. Sem contar a redundância quando se usa Repository, onde métodos do repositório são replicados no Service.
No DDD, a coisa é diferente. Repository pode ser acessado independente se ou não dentro de uma Service. Por exemplo, dentro do Controller. Não há violação de regras aqui. Service na verdade só é necessário quando você precisa implementar regras de negócios complexas que envolvem mais de um repositório, outros serviços etc. Nem tudo precisa e deve ser isolado em Services.
No Laravel, especialmente, acho um trabalho quase desnecessário dada a quantidade de possibilidades de desacoplamento (jobs, queues, events). O modelo de arquitetura mais próximo ao que o Laravel já oferece, nativamente, é o CQRS.
O Marcelo disse ainda, mais adiante:
Você pode ter serviços que resolvem um problema em específico, e não um serviço que implementa um "domínio" inteiro, como é comum encontrar. Exemplo: ao invés de UserService para resolver tudo relacionado ao usuário, criar uma classe UserRegistration como um único método 'register', para registrar um usuário, e resolver tudo relacionado a esse registro (enviar email, disparar eventos, registrar no log, etc).
O Victor disse de forma resumida um ponto importante: o acoplamento. Você não irá querer que a camada de serviço lide com questões de nível mais baixo. Ela deve usar a camada de repositório para trabalhar com o banco. Falarei sobre isso mais adiante.
Embora eu concorde com muito do que o Marcelo disse (e a resposta dele foi brilhante), tem um ponto citado que a literatura diz o contrário: de que no DDD o repositório pode ser acessado em qualquer lugar.
No livro Domain-Driven Design in PHP, no capítulo sobre Architectural Styles, a certa altura é dito o seguinte:
The
PostService
is what is known as an Application Service, and its purpose is to orchestrate and organize the Domain behavior. In other words, the Application services are the ones that make things happen, and they’re the direct clients of a Domain Model. No other type of object should be able to directly talk to the internal layers of the Model layer.
Ainda não li muita coisa do livro Domain-Driven Design: Tackling Complexity in the Heart of Software do Eric Evans para encontrar algum ponto que concorde com isso, mas se você pegar o código em Java que foi baseado no livro você vai ver que isso de certa forma também é aplicado: https://github.com/patrikfr/dddsample/blob/master/dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/interfaces/booking/facade/BookingServiceFacadeImpl.java#L48
E, bem, é exatamente isso que o Martin Fowler diz ao abordar a camada de serviços: https://martinfowler.com/eaaCatalog/serviceLayer.html
Fowler ao descrever a arquitetura CQRS também usa serviços como interfaces: https://martinfowler.com/bliki/CQRS.html
Se você parar para pensar, sob uma certa ótica isso está certo. Não deveria o controller ser responsável apenas por delegar as responsabilidades à outras classes e coordenar a interação entre elas? O ponto-chave aqui é que o controller nem sempre é o único consumidor - e aqui a orquestração pelo serviço de aplicação passa a fazer mais sentido. Mais adiante cito um exemplo que fica mais fácil entender.
Mas, claro, isso são características das arquiteturas DDD/CQRS. Fora delas você é livre para fazer o que quiser. :grin:
Sobre o serviço ficar inchado isso é resolvido exatamente como o Marcelo acrescentou mais adiante. O Eric Evans fala sobre isso no livro dele:
The name service emphasizes the relationship with other objects. Unlike ENTITIES and VALUE OBJECTS, it is defined purely in terms of what it can do for a client. A SERVICE tends to be named for an activity, rather than an entity – a verb rather than a noun. A SERVICE can still have an abstract, intentional definition; it just has a different flavor than the definition of an object. A SERVICE should still have a defined responsibility, and that responsibility and the interface fulfilling it should be defined as part of the domain model. Operation names should come from the UBIQUITOUS LANGUAGE or be introduced into it. Parameters and results should be domain objects.
É portanto uma boa prática que os serviços sejam orientados à atividades ao invés de entidades. :smile:
Mas como o Marcelo brilhantemente concluiu, muita coisa não precisa estar em serviços e no Laravel você tem várias opções para desacoplar seu código. A parte de eventos principalmente, seu domínio é suposto a ser rico em eventos tanto no DDD como no CQRS (onde eles são indispensáveis).
Agora permita-me abordar alguns pontos da sua pergunta:
Ou seja, algo como
registerNewSubscription
poderia estar tanto no repository como num caso de uso.
Sim e não. O papel do repositório de assinaturas aqui seria apenas salvar uma entidade de assinatura. O papel do serviço seria criar a instância dessa entidade, preencher os dados que recebeu do controller e enviar essa entidade ao repositório para salvá-la. Note que cada um tem uma responsabilidade bem delineada.
No grupo do Facebook a pergunta ficou um pouco diferente:
Como fica a relação entre Services (use cases) e métodos dos repositórios? Não sei se há uma confusão, mas, pra mim, soam como a mesma coisa em locais diferentes. Ou seja, algo como getAllAvailableUsers poderia estar tanto no repository como num caso de uso.
Esse caso em específico nos remete ao Criteria pattern. Porque você não quer que o seu repositório fique inchado, você certamente vai acabar implementando um método getAll()
que opcionalmente aceite um criteria que filtre isso conforme o caso. Aqui o serviço vai ser o responsável por orquestrar isso, ele vai passar o criteria ao repositório que por sua vez vai retornar tudo bonitinho. E o controller? Exatamente: ele não precisa saber nada disso.
Imagine um serviço ListUsers com os métodos all()
, pending()
, rejected()
, etc. Ele usará diferentes criterias e um único método do repositório para retornar os dados. Quem quer que consuma esse serviço (um controller, um comando, uma queue, qualquer coisa) só precisará saber qual método chamar. Toda orquestração das classes do domínio fica por conta do serviço de aplicação.
Agora fica mais fácil de entender o ponto de usar serviços como interface, correto? É basicamente isso: quem estiver consumindo não precisa conhecer detalhes do domínio e pode reutilizar os métodos em qualquer lugar. :grin:
Não sei se respondi tudo que gostaria mas acho que já esclarece alguns pontos menos claros do uso das camadas de serviços e repositórios. Se esqueci algum ponto pode perguntar que tentarei acrescentar a posteriori.
Abraços! :smile:
@paulofreitas Sensacional, cara!
Esse assunto é bem pertinente e, pelo menos no que observo, corresponde às muitas perguntas que fazem sobre o assunto.
Será que não cabe complementar com códigos de exemplo e fixar isso em algum lugar? (Aqui mesmo, Medium, wiki do repositório). Já havia sugerido algo do tipo há uns meses.
Lá no Facebook, o Eduardo Cesar comentou:
Cuidado ao relacionar Services ou Services Layer com SOA. SOA está muito mas muito além desses padrões, e SOA não é "um Serviço de Negócio (Uma classe de Serviço possui 1 ou mais métodos - cada método pode facilmente representar um Caso de Uso)" . SOA é um acrônimo para Service Oriented Architeture e envolve muitos outros conceitos além de uma classe.
Eu acho que repository tem como principal responsabilidade encapsular questões pertinentes a manipula de dados, independentes de sua origem. Deixando para objetos de domínio apenas suas responsabilidades. Ao usar Doctrine pode se notar a implementação do pattern repository lidando justamente com essas questões de dados, persistências e buscas que estão relacionadas á algum objeto de domínio.
Services, acredito que estes podem ou não ter dependências com repositórios, não há uma regra aqui (bom, ao menos até onde eu saiba....), mas entendo como services tarefas complexas que não se enquadram em um objeto de domínio unico ou que tão pouco pode ser resolvido em uma busca ou persistência de dados.
Services Layer, que busca centralizar e prover classes de serviços para a aplicação, tem essa palestra do guilherme blanco sobre o assunto.... e é bem didática...https://pt.slideshare.net/guilhermeblanco/service-layer.
São coisas distintas, SOA, Repository, Services e Service Layer, o ideal é compreender e buscar em outras linguagens e frameworks as diferentes implementações que possam existir.
Seria uma boa prática por exemplo, criar um service que salva por exemplo um usuário, e ali mesmo dispara um evento/job para enviar email ou chama a facade que envia email direto ?
Entao, a partir do usuario valido voce pode usar o metodo notify do model pra mandar o email...
@paulofreitas Sensacional, cara!
Esse assunto é bem pertinente e, pelo menos no que observo, corresponde às muitas perguntas que fazem sobre o assunto.
Será que não cabe complementar com códigos de exemplo e fixar isso em algum lugar? (Aqui mesmo, Medium, wiki do repositório). Já havia sugerido algo do tipo há uns meses.
É uma boa idéia, explorando isso um tanto mais além daria para criar uma espécie de "livro" colaborativo sobre, o PHP: The Right Way nasceu assim. :stuck_out_tongue: No caso haveria de ter um repositório próprio para que as issues não se misturem, dependendo do caso até uma organização própria. De qualquer modo é algo que vale a pena planejar bem antes. :thinking: O problema ao menos de minha parte tem sido o tal do tempo... As 24 horas do dia andam sendo insuficientes. :confused:
Vamos ver isso?
Eu acho muito importante e me surpreendo com o fato de ainda não termos nada desse tipo, já que há uma grande recorrência nesse tipo de pergunta.
O que acham de criar um repositório laravel-ddd-example
(ou de nome similar) e começarmos a montar a estrutura? Assim, o pessoal pode ir mandando seus PRs, criando issues para discutir, etc.
Ideia excelente.
Em 10 de agosto de 2017 16:32, Guilherme Augusto Henschel < notifications@github.com> escreveu:
O que acham de criar um repositório laravel-ddd-example (ou de nome similar) e começarmos a montar a estrutura? Assim, o pessoal pode ir mandando seus PRs, criando issues para discutir, etc.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/laravelbrasil/forum/issues/126#issuecomment-321651109, or mute the thread https://github.com/notifications/unsubscribe-auth/ACs-SS-TFeFQ73ghacMIKq0mCWV9de9sks5sW1rhgaJpZM4OyeDe .
Um pouco fora da curva, mas ainda dentro da discussão sobre Repositories, o que acham das opiniões expressas neste post do Laracast: Reasons I abandoned repository pattern in Laravel
Aos colegas mais experientes, o que acham de tudo que foi apontado neste post? Confesso que fiquei confuso 😄
As pessoas tem uma pessima mania de achar q porque uma solucao serviu para uma situacao, ela serve pra tudo, não é bem assim. Se fosse assim, nao existiriam varios padrões.
2017-11-11 17:04 GMT-02:00 Tiago Silva Pereira notifications@github.com:
Um pouco fora da curva, mas ainda dentro da discussão sobre Repositories, o que acham das opiniões expressas neste post do Laracast: Reasons I abandoned repository pattern in Laravel https://laracasts.com/discuss/channels/general-discussion/reasons-i-abondoned-repository-pattern-in-laravel?page=1
Aos colegas mais experientes, o que acham de tudo que foi apontado neste post? Confesso que fiquei confuso 😄
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/laravelbrasil/forum/issues/126#issuecomment-343686845, or mute the thread https://github.com/notifications/unsubscribe-auth/ACs-SQ_Qw74xjryVpgWhNEbpmhuutvIeks5s1e_BgaJpZM4OyeDe .
Verdade @IgorDePaula, faz sentido. Basicamente, o correto seria utilizar o melhor padrão para determinada situação né?
Vamos reacender as chamas no assunto repository? =D
Uma pergunta que ia fazer lá no tópico que foi apagado (e recuperado), era a seguinte: Como fica a relação entre Services (use cases) e métodos os repositórios? Não sei se há uma confusão, mas, pra mim, soam como a mesma coisa em locais diferentes. Ou seja, algo como
registerNewSubscription
poderia estar tanto no repository como num caso de uso.O para o exemplo (que modifiquei por causa dos comentários aqui e no facebook), o registro de uma nova assinatura no sistema é uma tarefa complexa, que envolve facilmente:
Ou seja, podemos ter algo como:
ou
Nesse cenário, onde seria melhor aplicar? E se for em casos de uso, o repository não perde o seu papel e passa a ser só mais uma camada?
Eu opto pelo Service por ter uma classe com uma única responsabilidade, por ficar mais fácil pra quem for utilizar e pra não inflar o repository.
Tenho outras questões com relação a isso, mas comento de acordo com as respostas.