Então, depois de ler "Implementando o design orientado a domínio por Vaughn Vernon", decidi refatorar meu código para obter melhor reutilização, isolando o que eu acredito serem os conceitos principais de domínio em módulos separados.
Cada módulo contém seu próprio conjunto de camadas arquiteturais distintas, que incluem as camadas Domínio, Infra-estrutura e Aplicativo / Apresentação (por recomendação de Vaughn, decidi separar ainda mais as responsabilidades da camada Aplicativo das rotas, controladores MVC + modelos existentes no diretório Camada de apresentação).
Decidi colocar cada uma dessas camadas dentro de seu próprio pacote; e cada pacote está referenciando a camada abaixo dela como uma dependência. ou seja: a camada de apresentação depende da camada de aplicativo, a aplicação depende de infraestrutura etc. Como o repositório faz parte do domínio, cada interface de repositório existe dentro da camada / pacote do domínio, sendo a implementação uma responsabilidade da camada / pacote de infraestrutura (Doctrine etc).
Espero que, ao reestruturar meu código dessa maneira, seja possível trocar a camada de aplicativos e reutilizar meu domínio em vários aplicativos da web.
Finalmente, o código parece que está começando a se moldar novamente, mas o que ainda me confunde é essa distinção entre aplicativos, infraestrutura e serviços de domínio.
Um exemplo comum de um serviço de domínio é algo que você usaria para senhas de hash. Isso faz sentido para mim do ponto de vista do SRP, pois a entidade Usuário não deve se preocupar com os muitos algoritmos de hash diferentes que podem ser usados para armazenar as credenciais de um usuário.
Então, com isso em mente, tratei esse novo serviço de Domínio da mesma maneira que meus Repositórios; definindo uma interface no domínio e deixando a implementação na camada Infraestrutura. No entanto, agora estou pensando no que deve ser feito com os Serviços de Aplicativo.
Como está agora, cada Entidade possui seu próprio Serviço de Aplicativo, ou seja, a Entidade do Usuário possui um UserService na Camada de Aplicativo. O UserService nesse caso é responsável por analisar tipos de dados primitivos e manipular um caso de uso comum "UserService :: CreateUser (nome da string, email da string, etc): Usuário.
O que me preocupa é o fato de precisar reimplementar essa lógica em vários aplicativos, caso decida trocar a camada de aplicativos. Então eu acho que isso me leva às minhas próximas perguntas:
Os Serviços de Domínio são apenas uma Interface que existe para fornecer uma camada de abstração entre a Camada de Infra-estrutura e seu Modelo? ou seja: Repositórios + Serviços de hash, etc.
Eu mencionei ter um Serviço de Aplicativo que se parece com isso:
Acesso / Aplicativo / Serviços / UserService :: CreateUser (nome da string, email da string, etc): User
A assinatura do método aceita argumentos de tipo de dados primitivos e retorna uma nova Entidade do Usuário (não um DTO!).
Isso pertence à camada Infraestrutura como uma implementação de alguma interface definida na camada Domínio ou a Camada de Aplicação é de fato mais apropriada devido a argumentos primitivos do tipo de dados, etc. ?
exemplo:
Access/Domain/Services/UserServiceInterface
e
Access/Infrastructure/Services/UserService implements UserServiceInterface
Como os módulos separados devem lidar com relacionamentos unidirecionais. O módulo A deve referir a camada de aplicação do módulo B (como está fazendo agora) ou a implementação da infraestrutura (via interface separada)?
Os serviços de camada de aplicativo exigem uma interface separada? Se a resposta for sim, então onde eles devem estar localizados?
Respostas:
As responsabilidades dos serviços de domínio incluem várias coisas. O mais óbvio é a lógica de moradia que não se encaixa em uma única entidade. Por exemplo, pode ser necessário autorizar um reembolso para uma determinada compra, mas para concluir a operação, você precisa de dados da
Purchase
entidade,Customer
entidade,CustomerMembership
entidade.Os serviços de domínio também fornecem operações necessárias ao domínio para concluir sua funcionalidade, como
PasswordEncryptionService
, por exemplo , mas a implementação desse serviço residirá na camada de infraestrutura, pois será principalmente uma solução técnica.Serviços de infra-estrutura são serviços que permitem uma operação de infraestrutura, como abrir uma conexão de rede, copiar arquivos do sistema de arquivos, conversar com um serviço da Web externo ou conversar com o banco de dados.
Serviços de aplicativo são a implementação de um caso de uso no aplicativo que você está construindo. Se você estiver cancelando uma reserva de voo, você:
A camada do aplicativo é o cliente do domínio. O domínio não tem idéia de qual é o seu caso de uso. Apenas expõe a funcionalidade por meio de seus agregados e serviços de domínio. A camada de aplicativo, no entanto, reflete o que você está tentando alcançar, orquestrando as camadas de domínio e infraestrutura.
O PHP pode não ser o melhor lugar para começar a aprender sobre DDD, pois muitas das estruturas PHP existentes no mercado (Laravel, Symfony, Zend, etc. etc) tendem a promover o RAD. Eles estão mais focados no CRUD e na tradução de formulários para entidades. CRUD! = DDD
Sua camada de apresentação deve ser responsável por ler as entradas do formulário do objeto de solicitação e chamar o serviço de aplicativo correto. O serviço de aplicativo criará o usuário e chamará o repositório do usuário para salvar o novo usuário. Opcionalmente, você pode retornar um DTO do usuário de volta à camada de apresentação.
O módulo de palavras no idioma DDD tem um significado diferente do que você está descrevendo. Um módulo deve abrigar conceitos relacionados. Por exemplo, um módulo de pedido na camada de domínio pode abrigar os agregados de pedidos, a entidade OrderItem, OrderRepositoryInterface e MaxOrderValidationService.
Um módulo Order na camada do aplicativo pode abrigar OrderApplicationServie, CreateOrderCommand e OrderDto.
Se você está falando sobre camadas, cada camada deve preferencialmente depender das interfaces de outras camadas sempre que possível. A camada de apresentação deve depender das interfaces da camada de aplicação. A camada de aplicativo deve fazer referência às interfaces dos repositórios ou serviços de domínio.
Pessoalmente, não crio interfaces para entidades e objetos de valor porque acredito que as interfaces devem estar relacionadas a um comportamento, mas YMMV :)
Depende :) para aplicações complexas, construo interfaces porque aplicamos testes rigorosos de unidade, integração e aceitação. O acoplamento fraco é fundamental aqui e as interfaces estão na mesma camada (camada de aplicação).
Para aplicativos simples, eu construo contra os serviços de aplicativos diretamente.
fonte
Hmm, resposta curta a uma pergunta longa, mas vejo o padrão da seguinte maneira
Regra 1: os objetos de domínio devem ter uma única raiz agregada
Regra 2: raízes agregadas não devem ser muito grandes, divida as coisas em contextos limitados
Problema: As raízes agregadas logo se tornam muito grandes e não há uma maneira clara de traçar uma linha entre os vários modelos de domínio neles
Solução: Serviços de Domínio. Crie interfaces que você pode injetar nos modelos de domínio que lhes permitam fazer coisas fora do contexto de domínio ou raiz agregada.
Então, eu acho que diria que seus exemplos são apenas Serviços / Repositórios normais, etc, IDatabaseRepositoryForStoringUsers ou IGenericHashingCode
Um serviço de domínio permite a comunicação entre contextos limitados. ie
Onde Usuários e Contas estão em Raízes Agregadas / Contextos Limitados separados.
Se o usuário e a conta estiverem na mesma raiz agregada, é claro que você poderá:
Não estou totalmente claro da sua pergunta sobre como você está integrando o material nTier Application / Infrastructure e o módulo. Obvs, você realmente não deseja nenhuma referência cruzada entre contextos vinculados, portanto, você colocaria suas interfaces de serviço de domínio em seu próprio módulo, que não faz referência a nenhum outro contexto vinculado. limitando você a expor tipos de valor base ou apenas DTOs possíveis
fonte