Conception pilotée par domaine: service de domaine, service d'application

268

Quelqu'un peut-il expliquer la différence entre les services de domaine et d'application en fournissant des exemples? Et, si un service est un service de domaine, est-ce que je mettrais l'implémentation réelle de ce service dans l'assembly de domaine et si oui, est-ce que j'injecterais également des référentiels dans ce service de domaine? Certaines informations seraient vraiment utiles.

Chris
la source

Réponses:

358

Services sont disponibles en 3 saveurs: Domain Services , Services d' application et l' infrastructure des services .

  • Services de domaine : encapsule une logique métier qui ne rentre pas naturellement dans un objet de domaine et n'est PAS des opérations CRUD typiques - celles-ci appartiendraient à un référentiel .
  • Services d'application : utilisés par des consommateurs externes pour parler à votre système (pensez aux services Web ). Si les consommateurs ont besoin d'accéder aux opérations CRUD, ils seraient exposés ici.
  • Services d'infrastructure : utilisés pour résumer les problèmes techniques (par exemple, MSMQ, fournisseur de messagerie, etc.).

Garder les services de domaine avec vos objets de domaine est judicieux - ils sont tous axés sur la logique du domaine. Et oui, vous pouvez injecter des référentiels dans vos services.

Les services d'application utilisent généralement les services de domaine et les référentiels pour traiter les demandes externes.

J'espère que cela pourra aider!

Vijay Patel
la source
2
Où mettriez-vous les commandes et les requêtes du CQRS? Quel service les génère et quel service les gère?
inf3rno
5
Je pense que les services d'application devraient être indépendants des détails techniques comme les "services Web", ils sont utilisés par ces services. Voir les services en conception pilotée par domaine
Deamon
1
@prograhammer - Un exemple de service de domaine pourrait être FundsTransferService, où le modèle de domaine est un BankAccount, le transfert pourrait avoir une logique métier qui ne rentre pas directement dans un objet de compte (extrait du livre Evans DDD).
BornToCode
disons par exemple que Loginuser () serait un exemple de service de domaine. où as getUsers () serait un service d'application ??
filthy_wizard
Les deux sont plutôt des services d'application car l'authentification et souvent aussi les décisions d'autorisation n'appartiennent pas au domaine principal.
MauganRa
114

(Si vous n'avez pas envie de lire, il y a un résumé en bas :-)

Moi aussi, j'ai eu du mal avec la définition précise des services d'application. Bien que la réponse de Vijay ait été très utile à mon processus de réflexion il y a un mois, je suis en désaccord avec une partie de celle-ci.

Autres ressources

Il y a très peu d'informations sur les services d'application. Des sujets tels que les racines agrégées, les référentiels et les services de domaine sont abordés en détail, mais les services d'application ne sont mentionnés que brièvement ou sont complètement exclus.

L'article MSDN Magazine An Introduction To Domain-Driven Design décrit les services d'application comme un moyen de transformer et / ou d'exposer votre modèle de domaine à des clients externes, par exemple en tant que service WCF. C'est ainsi que Vijay décrit également les services d'application. De ce point de vue, les services applicatifs sont une interface avec votre domaine .

Les articles de Jeffrey Palermo sur l'architecture de l'oignon ( première , deuxième et troisième parties ) sont une bonne lecture. Il traite les services d' application comme des concepts au niveau de l' application , comme une session utilisateur. Bien que cela soit plus proche de ma compréhension des services d'application, cela ne correspond toujours pas à mes réflexions sur le sujet.

Mes pensées

J'en suis venu à considérer les services d'application comme des dépendances fournies par l'application . Dans ce cas, l'application peut être une application de bureau ou un service WCF.

Domaine

Temps pour un exemple. Vous commencez avec votre domaine. Toutes les entités et tous les services de domaine qui ne dépendent pas de ressources externes sont implémentés ici. Tous les concepts de domaine qui dépendent de ressources externes sont définis par une interface. Voici une disposition de solution possible (nom du projet en gras):

Ma solution
- My.Product.Core (My.Product.dll)
  - DomainServices
      IExchangeRateService
    Produit
    ProductFactory
    IProductRepository

Les classes Productet ProductFactoryont été implémentées dans l'assembly de base. C'est IProductRepositoryquelque chose qui est probablement soutenu par une base de données. La mise en œuvre de ceci n'est pas du ressort du domaine et est donc définie par une interface.

Pour l'instant, nous allons nous concentrer sur le IExchangeRateService. La logique métier de ce service est implémentée par un service Web externe. Cependant, son concept fait toujours partie du domaine et est représenté par cette interface.

Infrastructure

L'implémentation des dépendances externes fait partie de l'infrastructure de l'application:

Ma solution
+ My.Product.Core (My.Product.dll)
- My.Product.Infrastructure (My.Product.Infrastructure.dll)
  - DomainServices
      XEExchangeRateService
    SqlServerProductRepository

XEExchangeRateServiceimplémente le IExchangeRateServiceservice de domaine en communiquant avec xe.com . Cette implémentation peut être utilisée par vos applications qui utilisent votre modèle de domaine, en incluant l'assembly d'infrastructure.

Application

Notez que je n'ai pas encore mentionné les services d'application. Nous allons les regarder maintenant. Disons que nous voulons fournir une IExchangeRateServiceimplémentation qui utilise un cache pour des recherches rapides. Le plan de cette classe de décorateur pourrait ressembler à ceci.

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.
}

Remarquez le ICacheparamètre? Ce concept ne fait pas partie de notre domaine, il ne s'agit donc pas d'un service de domaine. C'est un service d'application . C'est une dépendance de notre infrastructure qui peut être fournie par l'application. Introduisons une application qui le démontre:

Ma solution
- My.Product.Core (My.Product.dll)
  - DomainServices
      IExchangeRateService
    Produit
    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

Tout cela se réunit dans l'application comme ceci:

// 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);

Résumé

Une application complète se compose de trois couches principales:

  • domaine
  • Infrastructure
  • application

La couche de domaine contient les entités de domaine et les services de domaine autonomes. Tous les concepts de domaine (cela inclut les services de domaine, mais aussi les référentiels) qui dépendent de ressources externes, sont définis par des interfaces.

La couche infrastructure contient l'implémentation des interfaces à partir de la couche domaine. Ces implémentations peuvent introduire de nouvelles dépendances hors domaine qui doivent être fournies à l'application. Ce sont les services d'application et sont représentés par des interfaces.

La couche application contient l'implémentation des services d'application. La couche application peut également contenir des implémentations supplémentaires d'interfaces de domaine, si les implémentations fournies par la couche infrastructure ne sont pas suffisantes.

Bien que cette perspective puisse ne pas correspondre à la définition DDD générale des services, elle sépare le domaine de l'application et vous permet de partager l'assemblage de domaine (et d'infrastructure) entre plusieurs applications.

Niels van der Rest
la source
2
@ dario-g: vous devez reconstruire / repeupler votre modèle de domaine à partir du modèle de demande et transmettre le modèle de domaine au service de domaine. Cette question peut vous fournir quelques idées. Sinon, faites-le moi savoir et je verrai si j'ai le temps d'ajouter une réponse à l'autre question.
Niels van der Rest
1
@Tiendq: Vous voulez dire l' IExchangeRateServiceinterface? Il s'agit d'un concept de domaine, c'est-à-dire quelque chose qui est inclus dans le langage omniprésent de votre client. D'autres parties de votre domaine peuvent dépendre de ce service, c'est pourquoi son interface est définie dans la couche domaine. Mais parce que son implémentation implique un service Web externe, la classe d'implémentation réside dans la couche infrastructure. De cette façon, la couche de domaine ne concerne que la logique métier.
Niels van der Rest
4
@Tiendq: Dans une architecture en couches traditionnelle, l'infrastructure est généralement indépendante du domaine. Mais dans l'architecture Onion (voir les liens dans ma réponse), l'infrastructure implémente les dépendances externes du domaine. Mais je ne dirais pas que l'infrastructure dépend du domaine, elle y fait simplement référence . J'ai pris le terme «infrastructure» de l'architecture d'Oignon, mais «externes» peut être un meilleur nom.
Niels van der Rest
1
@Derek: Une de ces «choses» pourrait être une ExchangeRateinstance, qui contient une devise de base, une contre-devise et la valeur du taux de change entre ces deux devises. Ces valeurs étroitement liées représentent le concept de «taux de change» du domaine, elles vivent donc dans la couche du domaine. Bien qu'il puisse sembler être un simple DTO, dans DDD, il est appelé un objet de valeur et il peut contenir une logique métier supplémentaire pour comparer ou transformer des instances.
Niels van der Rest du
6
Je suis en désaccord avec la partie où vous n'êtes pas d'accord avec Vijay et voici pourquoi. CachingExchangeRateService est une préoccupation d'infrastructure. Même si vous acceptez génériquement un ICache, l'implémentation de ce ICache dépend de la technologie impliquée (par exemple, Web, Windows). Ce n'est pas parce qu'il est générique que c'est un service d'application. Un service d'application est l'API de votre domaine. Et si vous vouliez révéler votre domaine à quelqu'un d'autre qui rédige une application, que vont-ils utiliser? Services d'application, et ils ne peuvent pas besoin en cache de sorte que votre cache impl est inutile de les (ie.why son infrastructure)
Hawkins Aaron
38

La meilleure ressource qui m'a aidé à comprendre la différence entre un service d'application et un service de domaine était l'implémentation java de l'exemple de fret d'Eric Evans, trouvé ici . Si vous le téléchargez, vous pouvez consulter les éléments internes de RoutingService (un service de domaine) et de BookingService, CargoInspectionService (qui sont des services d'application).

Mon moment «aha» a été déclenché par deux choses:

  • Lire la description des Services dans le lien ci-dessus, plus précisément cette phrase:

    Les services de domaine sont exprimés en termes de langage omniprésent et de types de domaine, c'est-à-dire que les arguments de méthode et les valeurs de retour sont des classes de domaine appropriées.

  • Lire ce billet de blog , en particulier cette partie:

    Ce que je trouve d'une grande aide pour séparer les pommes des oranges, c'est penser en termes de workflow d'application. Toute logique concernant le flux de travail d'application finit généralement par être des services d'application pris en compte dans la couche application, tandis que les concepts du domaine qui ne semblent pas correspondre en tant qu'objets de modèle finissent par former un ou plusieurs services de domaine.

Ghola
la source
3
Je suis d'accord, c'est exactement ainsi que je définis les services d'application, et cela correspond à toutes les situations que j'ai rencontrées jusqu'à présent. Les services de domaine traitent de tout ce qui concerne les objets de domaine, mais qui dépassent le cadre d'une seule entité. Ex: BookReferencesService.GetNextAvailableUniqueTrackingNumber (), l'accent est clairement mis sur les règles métier *. En ce qui concerne Application Service, c'est exactement ce que vous décrivez, la plupart du temps je commence par mettre ce flux de travail métier dans mes actions de contrôleur, et quand je le remarque, je refactorise cette logique dans la couche de service d'application. On pourrait dire que cette couche est destinée aux cas d'utilisation
tobiak777
1
* Et ces interfaces de service de domaine sont consommées par les entités de domaine.
tobiak777
32

Le service de domaine est l'extension du domaine. Il ne doit être vu que dans le contexte du domaine. Ce n'est pas une action de l'utilisateur comme par exemple fermer un compte ou quelque chose. Le service de domaine s'adapte là où il n'y a pas d'État. Sinon, ce serait un objet de domaine. Le service de domaine fait quelque chose qui n'a de sens que lorsqu'il est effectué avec d'autres collaborateurs (objets de domaine ou autres services). Et ce sens est la responsabilité d'une autre couche.

Le service d'application est cette couche qui initialise et supervise l'interaction entre les objets de domaine et les services. Le flux est généralement le suivant: obtenir un ou plusieurs objets de domaine à partir du référentiel, exécuter une action et les (les) remettre là-bas (ou non). Il peut faire plus - par exemple, il peut vérifier si un objet de domaine existe ou non et lever des exceptions en conséquence. Il permet donc à l'utilisateur d'interagir avec l'application (et c'est probablement de là que son nom provient) - en manipulant les objets et services de domaine. Les services d'application doivent généralement représenter tous les cas d'utilisation possibles. La meilleure chose à faire avant de penser au domaine est probablement de créer des interfaces de service d'application qui vous donneront une bien meilleure idée de ce que vous essayez vraiment de faire. Avoir de telles connaissances vous permet de vous concentrer sur le domaine.

Les référentiels peuvent généralement être injectés dans les services de domaine, mais c'est un scénario plutôt rare. C'est la couche application qui le fait la plupart du temps.

kboom
la source
10
"Le service de domaine s'adapte là où il n'y a pas d'état. Sinon, ce serait un objet de domaine." l'a fait cliquer pour moi. Je vous remercie.
Nick
32

Du livre rouge (Implémentation de la conception pilotée par le domaine, par Vaughn Vernon), voici comment je comprends les concepts:

Les objets de domaine ( entités et objets de valeur ) encapsulent le comportement requis par le (sous) domaine, le rendant naturel, expressif et compréhensible.

Les services de domaine encapsulent de tels comportements qui ne tiennent pas dans un seul objet de domaine. Par exemple, une bibliothèque de livres prêtant un Bookà un Client(avec les Inventorymodifications correspondantes ) peut le faire à partir d'un service de domaine.

Les services d'application gèrent le flux des cas d'utilisation, y compris toutes les préoccupations supplémentaires nécessaires en plus des domaines. Il expose souvent de telles méthodes via son API, pour la consommation de clients externes. Pour s'appuyer sur notre exemple précédent, notre service d'application peut exposer une méthode LendBookToClient(Guid bookGuid, Guid clientGuid)qui:

  • Récupère le Client.
  • Confirme ses autorisations. ( Notez comment nous avons gardé notre modèle de domaine exempt de problèmes de sécurité / gestion des utilisateurs. Une telle pollution pourrait entraîner de nombreux problèmes. Au lieu de cela, nous remplissons cette exigence technique ici, dans notre service d'application. )
  • Récupère le Book.
  • Appelle le service de domaine (en passant le Clientet Book) pour gérer la logique de domaine réelle de prêt du livre au client. Par exemple, j'imagine que confirmer la disponibilité du livre fait définitivement partie de la logique du domaine.

Un service d'application doit généralement avoir un flux très simple. Les flux de services d'application complexes indiquent souvent que la logique du domaine a fui hors du domaine.

Comme vous pouvez l'espérer, le modèle de domaine reste très propre de cette façon et est facile à comprendre et à discuter avec les experts du domaine, car il ne contient que ses propres préoccupations commerciales réelles. Le flux d'application , d'autre part, est également beaucoup plus facile à gérer, car il est débarrassé des problèmes de domaine, et devient concis et simple.

Timo
la source
3
Je dirais que le service d'application est également le point où les dépendances sont résolues. Sa méthode est un cas d'utilisation, un flux unique, afin qu'il puisse prendre des décisions éclairées sur les implémentations concrètes à utiliser. Les transactions de base de données conviennent également ici.
Timo
10

Services de domaine: les méthodes qui ne tiennent pas vraiment sur une seule entité ou qui nécessitent un accès au référentiel sont contenues dans les services de domaine. La couche de service de domaine peut également contenir sa propre logique de domaine et fait autant partie du modèle de domaine que les entités et les objets de valeur.

Services d'application: le service d'application est une couche mince qui se trouve au-dessus du modèle de domaine et coordonne l'activité de l'application. Il ne contient pas de logique métier et ne détient l'état d'aucune entité; cependant, il peut stocker l'état d'une transaction de workflow métier. Vous utilisez un service d'application pour fournir une API dans le modèle de domaine à l'aide du modèle de messagerie de demande-réponse.

Millett, C (2010). Modèles de conception ASP.NET professionnels. Éditions Wiley. 92.

GorkemHalulu
la source
7

Services de domaine : service qui exprime une logique métier qui ne fait partie d'aucune racine agrégée.

  • Vous avez 2 agrégats:

    • Product qui contient le nom et le prix.
    • Purchase qui contient la date d'achat, la liste des produits commandés avec la quantité et le prix du produit à ce moment-là, et le mode de paiement.
  • Checkout ne fait partie d'aucun de ces deux modèles et est un concept dans votre entreprise.

  • Checkoutpeut être créé en tant que service de domaine qui récupère tous les produits et calcule le prix total, paie le total en appelant un autre service de domaine PaymentServiceavec une partie de mise en œuvre de l'infrastructure et le convertit en Purchase.

Services d'application : service qui "orchestre" ou exerce des méthodes de domaine. Cela peut être aussi simple que votre contrôleur.

C'est l'endroit où vous faites habituellement:

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);
}

Vous pouvez faire des validations ici comme vérifier si un Productest unique. À moins qu'un Productêtre unique ne soit un invariant, cela devrait faire partie du service de domaine qui pourrait être appelé UniqueProductCheckercar il ne peut pas faire partie de la Productclasse et il interagit avec plusieurs agrégats.

Voici un exemple complet du projet DDD: https://github.com/VaughnVernon/IDDD_Samples

Vous pouvez trouver de nombreux exemples de service d'application et quelques services de domaine

n'a pas d'importance
la source
Est-il obligatoire de valider et d'enregistrer des entités uniquement dans Application Services? Si j'ai des entités A, B et C et toutes liées les unes aux autres (A -> B -> C) et que l'opération sur A devrait provoquer des changements dans B et C en appelant un service de domaine à partir d'un autre, comment faire?
MrNVK
> Est-il obligatoire de valider et d'enregistrer des entités uniquement dans Application Services? Si vous le devez, alors oui. La plupart du temps, vous devez vérifier si un ID existe, sinon vous travaillerez sur une variable nulle.
doesnotmatter
1
> Si j'ai des entités A, B et C et toutes liées les unes aux autres (A -> B -> C) et que l'opération sur A devrait entraîner des changements dans B et C en appelant un service de domaine à partir d'un autre, comment le faire ? Je ne sais pas ce que vous entendez par "appeler un service de domaine à partir d'un autre", mais pour les réactions aux changements d'une entité, vous pouvez utiliser des événements ou simplement l'orchestrer avec le service d'application comme: ). Rechercher: Orchestration vs Chorégraphie
doesnotmatter
Merci pour votre réponse! "appeler un service de domaine à partir d'un autre" - je veux dire, si j'ai une opération complexe sur l'entité A, alors je dois utiliser ADomainService. Mais cette opération, en plus de l'entité A, affecte l'entité B. L'opération qui doit être effectuée sur l'entité B dans ADomainService est également complexe. Je dois donc utiliser BDomainService d'ADomainService. Maintenant, je doute de cette approche :) Mais si je mets cette logique dans ApplicationService, cela ne briserait-il pas l'encapsulation des processus métier qui ne devraient être que dans la couche domaine, pas dans la couche application?
MrNVK
Vous pouvez simplement émettre un événement à partir de votre service de domaine si vous pensez qu'il devrait se trouver dans un service de domaine au lieu du service d'application.
doesnotmatter
1

Pensez à un service de domaine comme un objet qui implémente une logique métier ou une logique liée aux règles métier sur les objets de domaine et cette logique est difficile à intégrer dans les mêmes objets de domaine et ne provoque pas non plus de changement d'état du service de domaine (le service de domaine est un objet sans un "état" ou mieux sans un état qui a une signification commerciale) mais finalement changer l'état seulement des objets de domaine sur lesquels opère.

Alors qu'un service d' application implémente une logique de niveau applicatif en tant qu'interaction utilisateur, validation d'entrée, logique non liée à l'entreprise mais à d'autres préoccupations: authentification, sécurité, emailing, etc., se limitant à utiliser simplement les services exposés par les objets de domaine.

Un exemple de ceci pourrait être le scénario suivant pensé uniquement pour expliquer le but: nous devons implémenter une très petite application utilitaire domotique qui exécute une opération simple, c'est-à-dire "allumer les lumières, quand quelqu'un ouvre la porte de la chambre d'une maison pour entrer et éteignez la lumière lorsque ferme la porte sortant de la pièce ".

Simplifiant beaucoup nous considérons seulement 2 entités de domaine: Dooret Lampchacune d'elles a 2 états, respectivement open/closedet respectivement on/off, et des méthodes spécifiques pour opérer des changements d'état sur elles.

Dans ce cas, nous avons besoin d'un service de domaine qui exécute l'opération spécifique d'allumer la lumière lorsque quelqu'un ouvre la porte de l'extérieur pour entrer dans une pièce, car la porte et les objets de la lampe ne peuvent pas implémenter cette logique d'une manière que nous considérons appropriée à leur nature .

Nous pouvons appeler notre service de domaine as DomoticDomainServiceet implémenter 2 méthodes: OpenTheDoorAndTurnOnTheLightet CloseTheDoorAndTurnOffTheLight, ces 2 méthodes changent respectablement l'état des deux objets Dooret Lampvers open/onet closed/off.

L'état d'entrée ou de sortie d'une salle, il n'est pas présent dans l'objet de service de domaine et non plus dans les objets de domaine, mais sera implémenté comme une simple interaction utilisateur par un service d'application, que nous pouvons appeler HouseService, qui implémente certains gestionnaires d'événements comme onOpenRoom1DoorToEnteretonCloseRoom1DoorToExit , et ainsi de suite pour chaque pièce (ce n'est qu'un exemple pour expliquer le but ..) , qui concerneront respectivement les méthodes de service de domaine d'appel pour exécuter le comportement assisté (nous n'avons pas considéré l'entité Roomcar ce n'est qu'un exemple) .

Cet exemple, loin d'être une application bien conçue dans le monde réel, a le seul but (comme on l'a dit plus souvent) d'expliquer ce qu'est un service de domaine et sa différence par rapport à un service d'application, j'espère qu'il est clair et utile.

Ciro Corvino
la source
Ciro: Votre exemple n'est pas pratique et c'est très déroutant.
Morteza Azizi
Salut Morteza, pourriez-vous être plus précis? Le vôtre risque d'être seulement un «jugement» sans véritable argument. Merci
Ciro Corvino