Design Orientado a Domínio: Serviço de Domínio, Serviço de Aplicativo

268

Alguém pode explicar a diferença entre serviços de domínio e aplicativos fornecendo alguns exemplos? E, se um serviço for um serviço de domínio, eu colocaria a implementação real desse serviço no conjunto do domínio e, se sim, também injetaria repositórios nesse serviço de domínio? Algumas informações seriam realmente úteis.

Chris
fonte

Respostas:

358

Os serviços são de três tipos: Serviços de Domínio , Serviços de Aplicativo e Serviços de Infraestrutura .

  • Serviços de Domínio : Encapsula a lógica de negócios que não se encaixa naturalmente em um objeto de domínio e NÃO são operações CRUD típicas - elas pertenceriam a um Repositório .
  • Serviços de aplicativos : Usado por consumidores externos para conversar com seu sistema (pense em Serviços da Web ). Se os consumidores precisarem acessar operações CRUD, eles serão expostos aqui.
  • Serviços de infraestrutura : Usado para abstrair preocupações técnicas (por exemplo, MSMQ, provedor de email, etc.).

Manter os Serviços de Domínio junto com seus Objetos de Domínio é sensato - todos eles são focados na lógica do domínio. E sim, você pode injetar Repositórios nos seus Serviços.

O Application Services normalmente usa os Serviços de Domínio e os Repositórios para lidar com solicitações externas.

Espero que ajude!

Vijay Patel
fonte
2
Onde você colocaria os comandos e as consultas do CQRS? Qual serviço os gera e qual serviço os gerencia?
Inf3rno 20/09/2014
5
Eu acho que os Serviços de Aplicativo não precisam de detalhes técnicos como "serviços da Web", eles são usados ​​por esses serviços. Veja Serviços em Domain-Driven Design
deamon
1
@prograhammer - Um exemplo de serviço de domínio pode ser o FundsTransferService, onde o modelo de domínio é BankAccount, a transferência pode ter alguma lógica de negócios que não se encaixa diretamente em um objeto de conta (extraído do livro Evans DDD).
BornToCode 29/03
digamos, por exemplo, Loginuser () seria um exemplo de serviço de domínio. onde getUsers () seria um serviço de aplicação ??
Filthy_wizard 11/10
Ambos são serviços de aplicativos, pois as decisões de autenticação e de autorização também não pertencem ao domínio principal.
precisa saber é o seguinte
114

(Se você não quiser ler, há um resumo na parte inferior :-)

Eu também lutei com a definição precisa de serviços de aplicativos. Embora a resposta de Vijay tenha sido muito útil para o meu processo de pensamento há um mês, cheguei a discordar de parte dele.

Outros recursos

Há muito pouca informação sobre serviços de aplicativos. Assuntos como raízes agregadas, repositórios e serviços de domínio são discutidos extensivamente, mas os serviços de aplicativos são mencionados apenas brevemente ou são excluídos por completo.

O artigo da MSDN Magazine Uma Introdução ao Design Orientado a Domínio descreve os serviços de aplicativo como uma maneira de transformar e / ou expor seu modelo de domínio a clientes externos, por exemplo, como um serviço WCF. É assim que o Vijay também descreve os serviços de aplicativos. Desse ponto de vista, os serviços de aplicativos são uma interface para o seu domínio .

Os artigos de Jeffrey Palermo sobre a Onion Architecture (parte um , dois e três ) são uma boa leitura. Ele trata os serviços de aplicativo como conceitos no nível do aplicativo , como uma sessão do usuário. Embora isso esteja mais próximo da minha compreensão dos serviços de aplicativos, ainda não está alinhado com meus pensamentos sobre o assunto.

Meus pensamentos

Passei a pensar nos serviços de aplicativos como dependências fornecidas pelo aplicativo . Nesse caso, o aplicativo pode ser um aplicativo de desktop ou um serviço WCF.

Domínio

Hora de um exemplo. Você começa com o seu domínio. Todas as entidades e quaisquer serviços de domínio que não dependem de recursos externos são implementados aqui. Quaisquer conceitos de domínio que dependem de recursos externos são definidos por uma interface. Aqui está um layout de solução possível (nome do projeto em negrito):

Minha solução
- My.Product.Core (My.Product.dll)
  - DomainServices
      IExchangeRateService
    produtos
    ProductFactory
    IProductRepository

As classes Producte ProductFactoryforam implementadas na montagem principal. O IProductRepositoryé algo que provavelmente é apoiada por um banco de dados. A implementação disso não é uma preocupação do domínio e, portanto, é definida por uma interface.

Por enquanto, vamos nos concentrar no IExchangeRateService. A lógica de negócios para este serviço é implementada por um serviço da web externo. No entanto, seu conceito ainda faz parte do domínio e é representado por essa interface.

A infraestrutura

A implementação das dependências externas faz parte da infraestrutura do aplicativo:

Minha solução
+ My.Product.Core (My.Product.dll)
- My.Product.Infrastructure (My.Product.Infrastructure.dll)
  - DomainServices
      XEExchangeRateService
    SqlServerProductRepository

XEExchangeRateServiceimplementa o IExchangeRateServiceserviço de domínio comunicando-se com xe.com . Essa implementação pode ser usada por seus aplicativos que utilizam seu modelo de domínio, incluindo o conjunto da infraestrutura.

Inscrição

Observe que ainda não mencionei os serviços de aplicativos. Vamos ver isso agora. Digamos que queremos fornecer uma IExchangeRateServiceimplementação que use um cache para pesquisas rápidas. O esboço dessa classe de decorador pode ser assim.

public class CachingExchangeRateService : IExchangeRateService
{
    private IExchangeRateService service;
    private ICache cache;

    public CachingExchangeRateService(IExchangeRateService service, ICache cache)
    {
        this.service = service;
        this.cache = cache;
    }

    // Implementation that utilizes the provided service and cache.
}

Observe o ICacheparâmetro? Esse conceito não faz parte do nosso domínio, portanto não é um serviço de domínio. É um serviço de aplicativo . É uma dependência de nossa infraestrutura que pode ser fornecida pelo aplicativo. Vamos apresentar um aplicativo que demonstra isso:

Minha solução
- My.Product.Core (My.Product.dll)
  - DomainServices
      IExchangeRateService
    produtos
    ProductFactory
    IProductRepository
- My.Product.Infrastructure (My.Product.Infrastructure.dll)
  - ApplicationServices
      ICache
  - DomainServices
      CachingExchangeRateService
      XEExchangeRateService
    SqlServerProductRepository
- My.Product.WcfService (My.Product.WcfService.dll)
  - ApplicationServices
      MemcachedCache
    IMyWcfService.cs
  + MyWcfService.svc
  + Web.config

Tudo isso se reúne no aplicativo como este:

// Set up all the dependencies and register them in the IoC container.
var service = new XEExchangeRateService();
var cache = new MemcachedCache();
var cachingService = new CachingExchangeRateService(service, cache);

ServiceLocator.For<IExchangeRateService>().Use(cachingService);

Resumo

Um aplicativo completo consiste em três camadas principais:

  • domínio
  • a infraestrutura
  • inscrição

A camada de domínio contém as entidades de domínio e os serviços de domínio autônomo. Quaisquer conceitos de domínio (incluindo serviços de domínio, mas também repositórios) que dependem de recursos externos, são definidos por interfaces.

A camada de infraestrutura contém a implementação das interfaces da camada de domínio. Essas implementações podem introduzir novas dependências fora do domínio que precisam ser fornecidas ao aplicativo. Estes são os serviços de aplicativos e são representados por interfaces.

A camada de aplicativo contém a implementação dos serviços de aplicativo. A camada de aplicativo também pode conter implementações adicionais de interfaces de domínio, se as implementações fornecidas pela camada de infraestrutura não forem suficientes.

Embora essa perspectiva possa não corresponder à definição geral de serviços DDD, ela separa o domínio do aplicativo e permite compartilhar o conjunto de domínio (e infraestrutura) entre vários aplicativos.

Niels van der Rest
fonte
2
@ dario-g: você teria que reconstruir / repovoar seu modelo de domínio a partir do modelo de solicitação e passar o modelo de domínio para o serviço de domínio. Esta pergunta pode fornecer algumas idéias. Caso contrário, informe-me e verei se tenho tempo para adicionar uma resposta à outra pergunta.
Niels van der Rest
1
@Tiendq: Você quer dizer a IExchangeRateServiceinterface? Este é um conceito de domínio, ou seja, algo que está incluído na linguagem onipresente do seu cliente. Outras partes do seu domínio podem depender desse serviço, e é por isso que sua interface é definida na camada de domínio. Porém, como sua implementação envolve um serviço da web externo, a classe de implementação reside na camada de infraestrutura. Dessa forma, a camada de domínio se preocupa apenas com a lógica de negócios.
Niels van der Rest
4
@Tiendq: em uma arquitetura tradicional em camadas, a infraestrutura geralmente é independente de domínio. Mas na arquitetura Onion (veja os links na minha resposta) a infraestrutura implementa as dependências externas do domínio. Mas eu não diria que a infraestrutura depende do domínio, apenas faz referência a ele. Eu peguei o termo 'infraestrutura' da Onion Architecture, mas 'externos' pode ser um nome melhor.
Niels van der Rest
1
@Derek: Uma dessas coisas pode ser uma ExchangeRateinstância, que contém uma moeda base, uma moeda contrária e o valor da taxa de câmbio entre essas duas moedas. Esses valores fortemente relacionados representam o conceito de 'taxa de câmbio' do domínio, portanto, eles vivem na camada do domínio. Embora possa parecer um DTO simples, no DDD é chamado de Objeto de Valor e pode conter lógica de negócios adicional para comparar ou transformar instâncias.
Niels van der Rest
6
Eu discordo da parte em que você discorda de Vijay e aqui está o porquê. CachingExchangeRateService é uma preocupação de infraestrutura. Mesmo que você esteja aceitando genericamente um ICache, a implementação desse ICache depende da tecnologia envolvida (por exemplo, Web, Windows). Só porque é genérico, não o torna um serviço de aplicativo. Um serviço de aplicativo é a API do seu domínio. E se você quiser revelar seu domínio para outra pessoa que estiver escrevendo um aplicativo, o que eles usarão? Application Services, e eles podem não precisar de cache para que o seu cache impl é inútil para eles (ie.why-lo de infra-estrutura)
Aaron Hawkins
38

O melhor recurso que me ajudou a entender a diferença entre um Serviço de Aplicativo e um Serviço de Domínio foi a implementação em java do exemplo de carga de Eric Evans, encontrado aqui . Se você descarregá-lo, poderá verificar os internos do RoutingService (um Serviço de Domínio) e do BookingService, CargoInspectionService (que são Serviços de Aplicativo).

Meu momento de 'aha' foi desencadeado por duas coisas:

  • Lendo a descrição dos Serviços no link acima, mais precisamente esta frase:

    Os serviços de domínio são expressos em termos da linguagem onipresente e dos tipos de domínio, ou seja, os argumentos do método e os valores de retorno são classes de domínio apropriadas.

  • Lendo esta postagem do blog , especialmente esta parte:

    O que eu acho de grande ajuda para separar as maçãs das laranjas é pensar em termos de fluxo de trabalho do aplicativo. Toda a lógica referente ao fluxo de trabalho do aplicativo geralmente acaba sendo incluída nos Serviços de Aplicativo na Camada de Aplicativos, enquanto os conceitos do domínio que parecem não se encaixar como objetos de modelo acabam formando um ou mais Serviços de Domínio.

Ghola
fonte
3
Concordo, é exatamente assim que eu defino os Serviços de Aplicativo e ele se encaixa em todas as situações que conheci até agora. Os Serviços de Domínio lidam com tudo relacionado a objetos de domínio, mas que vão além do escopo de uma única entidade. Ex: BookReferencesService.GetNextAvailableUniqueTrackingNumber (), o foco é claramente as regras de negócios *. Em relação ao Serviço de Aplicativo, é exatamente o que você descreve, na maioria das vezes começo colocando esse fluxo de trabalho de negócios em minhas ações do controlador e, quando percebo, refatoro essa lógica na camada de serviço de aplicativo. Podemos dizer que essa camada é para casos de uso
tobiak777
1
* E essas interfaces de serviço de domínio são consumidas pelas entidades do domínio.
tobiak777
32

Serviço de domínio é a extensão do domínio. Isso deve ser visto apenas no contexto do domínio. Esta não é uma ação do usuário, como por exemplo, fechar conta ou algo assim. O serviço de domínio se encaixa onde não há estado. Caso contrário, seria um objeto de domínio. O serviço de domínio faz algo que só faz sentido quando realizado com outros colaboradores (objetos de domínio ou outros serviços). E que fazer sentido é responsabilidade de outra camada.

Serviço de aplicativo é a camada que inicializa e supervisiona a interação entre os objetos e serviços do domínio. O fluxo geralmente é assim: obtenha o objeto (ou objetos) do domínio do repositório, execute uma ação e coloque-os lá (ou não). Ele pode fazer mais - por exemplo, pode verificar se um objeto de domínio existe ou não e lançar exceções de acordo. Portanto, ele permite que o usuário interaja com o aplicativo (e provavelmente é daí que o nome se origina) - manipulando objetos e serviços de domínio. Os serviços de aplicativo geralmente devem representar todos os casos de uso possíveis. Provavelmente, a melhor coisa que você pode fazer antes de pensar no domínio é criar interfaces de serviço de aplicativo, o que lhe dará uma visão muito melhor do que você realmente está tentando fazer. Ter esse conhecimento permite que você se concentre no domínio.

Os repositórios geralmente podem ser injetados nos serviços de domínio, mas esse é um cenário bastante raro. É a camada de aplicação que faz isso na maioria das vezes.

kboom
fonte
10
"O serviço de domínio se encaixa onde não há estado. Caso contrário, seria um objeto de domínio." fez clique para mim. Obrigado.
Nick
32

No Red Book (Implementando o Design Orientado a Domínio, de Vaughn Vernon), é assim que eu entendo os conceitos:

Objetos de domínio ( entidades e objetos de valor ) encapsulam o comportamento exigido pelo (sub) domínio, tornando-o natural, expressivo e compreensível.

Os serviços de domínio encapsulam comportamentos que não cabem em um único objeto de domínio. Por exemplo, uma biblioteca de livros que empresta a Booka Client(com as Inventoryalterações correspondentes ) pode fazê-lo a partir de um serviço de domínio.

Os serviços de aplicativo lidam com o fluxo de casos de uso, incluindo quaisquer preocupações adicionais necessárias sobre os domínios. Geralmente expõe esses métodos por meio de sua API, para consumo por clientes externos. Para desenvolver nosso exemplo anterior, nosso serviço de aplicativo pode expor um método LendBookToClient(Guid bookGuid, Guid clientGuid)que:

  • Recupera o Client.
  • Confirma suas permissões. ( Observe como mantivemos nosso modelo de domínio livre de preocupações com segurança / gerenciamento de usuários. Essa poluição pode levar a muitos problemas. Em vez disso, cumprimos este requisito técnico aqui, em nosso serviço de aplicativo. )
  • Recupera o Book.
  • Chama o serviço de domínio (passando o Cliente Book) para manipular a lógica real do domínio de emprestar o livro ao cliente. Por exemplo, imagino que confirmar a disponibilidade do livro é definitivamente parte da lógica do domínio.

Um serviço de aplicativo geralmente deve ter um fluxo muito simples. Os fluxos complexos de serviços de aplicativos geralmente indicam que a lógica do domínio vazou do domínio.

Como você pode ver, o modelo de domínio permanece muito limpo dessa maneira e é fácil de entender e discutir com os especialistas em domínio, porque contém apenas suas próprias preocupações comerciais reais. O fluxo de aplicativos , por outro lado, também é muito mais fácil de gerenciar, pois é isento de preocupações com o domínio e se torna conciso e direto.

Timo
fonte
3
Eu diria que o serviço de aplicativo também é o ponto em que as dependências são resolvidas. Seu método é um caso de uso, um fluxo único, para que possa tomar decisões informadas sobre implementações concretas a serem usadas. As transações de banco de dados também se encaixam aqui.
Timo
10

Serviços de domínio: métodos que realmente não se encaixam em uma única entidade ou requerem acesso ao repositório estão contidos nos serviços de domínio. A camada de serviço de domínio também pode conter lógica de domínio própria e faz parte do modelo de domínio tanto quanto entidades e objetos de valor.

Serviços de aplicativos: O serviço de aplicativos é uma camada fina que fica acima do modelo de domínio e coordena a atividade do aplicativo. Ele não contém lógica de negócios e não contém o estado de nenhuma entidade; no entanto, ele pode armazenar o estado de uma transação de fluxo de trabalho comercial. Você usa um serviço de aplicativo para fornecer uma API ao modelo de domínio usando o padrão de sistema de mensagens Request-Reply.

Millett, C (2010). Padrões profissionais de design do ASP.NET. Wiley Publishing. 92

GorkemHalulu
fonte
7

Serviços de domínio : um serviço que expressa uma lógica comercial que não faz parte de nenhuma raiz agregada.

  • Você tem 2 agregados:

    • Product que contém nome e preço.
    • Purchase que contém a data da compra, a lista de produtos solicitados com quantidade e preço do produto naquele momento e forma de pagamento.
  • Checkout não faz parte de nenhum desses dois modelos e é conceito nos seus negócios.

  • Checkoutpode ser criado como um Serviço de Domínio que busca todos os produtos e calcula o preço total, paga o total chamando outro Serviço de Domínio PaymentServicecom uma parte de implementação da Infraestrutura e converte-o em Purchase.

Serviços de aplicativos : um serviço que "orquestra" ou exercita métodos de domínio. Isso pode ser tão simples quanto apenas o seu controlador.

Este é o lugar onde você costuma fazer:

public String createProduct(...some attributes) {
  if (productRepo.getByName(name) != null) {
    throw new Exception();
  }

  productId = productRepository.nextIdentity();

  product = new Product(productId, ...some attributes);

  productRepository.save(product);

  return productId.value();
  // or Product itself
  // or just void if you dont care about result
}

public void renameProduct(productId, newName) {
  product = productRepo.getById(productId);

  product.rename(newName);

  productRepo.save(product);
}

Você pode fazer validações aqui, como verificar se a Producté único. A menos que um Productser exclusivo seja invariável, ele deve fazer parte do Serviço de Domínio que pode ser chamado UniqueProductCheckerporque não pode fazer parte da Productclasse e interage com vários Agregados.

Aqui está um exemplo completo do projeto DDD: https://github.com/VaughnVernon/IDDD_Samples

Você pode encontrar muitos exemplos de Serviço de Aplicativo e alguns Serviços de Domínio

não importa
fonte
É obrigatório validar e salvar entidades apenas no Application Services? Se eu tenho entidades A, B e C e todas elas relacionadas entre si (A -> B -> C) e a operação em A devem causar alterações em B e C chamando um Serviço de Domínio de outro, como fazer isso?
MrNVK 17/07/19
> É obrigatório validar e salvar entidades apenas no Application Services? Se você precisar, então sim. Na maioria das vezes, é necessário verificar se existe um ID, caso contrário, você trabalhará em uma variável nula.
doesnotmatter
1
> Se eu tiver as entidades A, B e C e todas elas relacionadas entre si (A -> B -> C) e a operação em A deve causar alterações em B e C chamando um Serviço de Domínio de outro, como fazê-lo ? Não sei ao certo o que você quer dizer com "chamando um Serviço de Domínio de outro", mas, para reações a alterações de uma Entidade, você pode usar Eventos ou apenas orquestrá-lo com o serviço de Aplicativo, como: aggregateA.doOperation (), agregateB.doAnother ( ) Pesquisar: Orquestração vs Coreografia
nota 17/07
Obrigado por responder! "chamando um serviço de domínio de outro" - quero dizer, se eu tiver uma operação complexa na entidade A, preciso usar o ADomainService. Mas essa operação, além da entidade A, afeta a entidade B. A operação que deve ser executada na entidade B no ADomainService também é complexa. Então, eu tenho que usar o BDomainService do ADomainService. Agora duvido dessa abordagem :) Mas se eu colocar essa lógica no ApplicationService, isso não quebraria o encapsulamento de processos de negócios que deveriam estar apenas na camada de domínio, não na camada de aplicativo?
MrNVK 18/07/19
Você pode emitir um evento do seu Serviço de Domínio se achar que ele deve estar em um Serviço de Domínio, em vez de em Serviço de Aplicativo.
doesnotmatter
1

Pense em um Serviço de Domínio como um objeto que implementa lógica de negócios ou lógica relacionada a regras de negócios em objetos de domínio. Essa lógica é difícil de se encaixar nos mesmos objetos de domínio e também não causa alteração de estado do serviço de domínio (serviço de domínio é um objeto sem um "estado" ou melhor sem um estado que tenha um significado comercial) mas, eventualmente, altere o estado apenas dos objetos de domínio nos quais opera.

Enquanto um Serviço de Aplicativo implementa lógica de nível de aplicativo como interação do usuário, validação de entrada, lógica não relacionada aos negócios, mas a outras preocupações: autenticação, segurança, email, etc., limitando-se a simplesmente usar serviços expostos por objetos de domínio.

Um exemplo disso pode ser o seguinte cenário pensado apenas para fins explicativos: temos que implementar um aplicativo utilitário domótico muito pequeno que execute uma operação simples, ou seja, "acenda as luzes, quando alguém abre a porta do quarto de uma casa para entrar entre e apague a luz quando fechar a porta que sai da sala ".

Para simplificar muito, consideramos apenas duas entidades de domínio: Doore Lampcada uma delas possui dois estados, respeitosamente open/closede on/offmétodos específicos para operar alterações de estado nelas.

Nesse caso, precisamos de um serviço de domínio que execute a operação específica de acender a luz quando alguém abrir a porta do lado de fora para entrar em uma sala, porque a porta e os objetos da lâmpada não podem implementar essa lógica da maneira que consideramos adequada. a sua natureza .

Podemos chamar nosso serviço de domínio como DomoticDomainServicee implementar 2 métodos: OpenTheDoorAndTurnOnTheLighte CloseTheDoorAndTurnOffTheLight, esses 2 métodos alteram, respectivamente, o estado de ambos os objetos Doore Lamppara open/one closed/off.

O estado de entrada ou saída de uma sala que não está presente no objeto de serviço de domínio e nos objetos de domínio, mas será implementado como simples interação do usuário por um serviço de aplicativo, que podemos chamar HouseService, que implementa alguns manipuladores de eventos como onOpenRoom1DoorToEntere onCloseRoom1DoorToExitassim por diante para cada sala (este é apenas um exemplo para fins de explicação ..) , que se preocupam respectivamente com os métodos de serviço de domínio de chamada para executar o comportamento assistido (não consideramos a entidade Roomporque é apenas um exemplo) .

Este exemplo, longe de ser um aplicativo do mundo real bem projetado, tem o único objetivo (como foi dito mais vezes) de explicar o que é um Serviço de Domínio e sua diferença em relação a um Serviço de Aplicativo, espero que seja claro e útil.

Ciro Corvino
fonte
Ciro: Seu exemplo não é prático e é muito confuso.
Morteza Azizi 15/06
Oi Morteza, você poderia ser mais específico? Seus riscos são apenas um "julgamento" sem nenhum argumento real. Obrigado
Ciro Corvino