Domain Driven Design: Domain Service, Application Service


268

Qualcuno può spiegare la differenza tra servizi di dominio e applicazione fornendo alcuni esempi? E, se un servizio è un servizio di dominio, metterei l'implementazione effettiva di questo servizio all'interno dell'assembly di dominio e, in tal caso, inietterei anche i repository in quel servizio di dominio? Alcune informazioni sarebbero davvero utili.

Risposte:


358

I servizi sono disponibili in 3 versioni: servizi di dominio , servizi applicativi e servizi di infrastruttura .

  • Servizi di dominio : incapsula la logica aziendale che non rientra naturalmente in un oggetto di dominio e NON sono operazioni CRUD tipiche, che appartengono a un repository .
  • Servizi applicativi : utilizzati da utenti esterni per parlare con il tuo sistema (pensa ai servizi Web ). Se i consumatori hanno bisogno di accedere alle operazioni CRUD, sarebbero esposti qui.
  • Servizi infrastrutturali : utilizzati per sottrarre preoccupazioni tecniche (ad esempio MSMQ, provider di posta elettronica, ecc.).

Mantenere i servizi di dominio insieme ai propri oggetti di dominio è ragionevole: sono tutti focalizzati sulla logica del dominio. E sì, puoi inserire Repository nei tuoi Servizi.

I servizi applicativi in ​​genere utilizzano sia i servizi di dominio che i repository per gestire le richieste esterne.

Spero che aiuti!


2
Dove metteresti i comandi e le query di CQRS? Quale servizio li genera e quale servizio li gestisce?
inf3rno,

5
Penso che i servizi applicativi debbano essere indipendenti da dettagli tecnici come i "servizi web", che vengono utilizzati da tali servizi. Vedi Servizi in Domain-Driven Design
deamon

1
@prograhammer - Un esempio per un servizio di dominio potrebbe essere FundsTransferService, dove il modello di dominio è un BankAccount, il trasferimento potrebbe avere una logica aziendale che non si adatta direttamente a un oggetto account (tratto dal libro DDD di Evans).
BornToCode

quindi diciamo ad esempio che Loginuser () sarebbe un esempio di un servizio di dominio. dove come getUsers () sarebbe un servizio applicativo ??
filthy_wizard,

Entrambi sono piuttosto servizi applicativi perché l'autenticazione e spesso anche le decisioni di autorizzazione non appartengono al dominio principale.
MauganRa,

114

(Se non hai voglia di leggere, c'è un riassunto in fondo :-)

Anch'io ho lottato con la definizione precisa di servizi applicativi. Sebbene la risposta di Vijay sia stata molto utile per il mio processo di pensiero un mese fa, non sono d'accordo con una parte di esso.

Altre risorse

Ci sono pochissime informazioni sui servizi applicativi. Argomenti come radici aggregate, repository e servizi di dominio sono ampiamente discussi, ma i servizi applicativi vengono citati solo brevemente o del tutto esclusi.

L'articolo della rivista MSDN Un'introduzione al design guidato dal dominio descrive i servizi applicativi come un modo per trasformare e / o esporre il modello di dominio a client esterni, ad esempio come servizio WCF. Ecco come Vijay descrive anche i servizi applicativi. Da questo punto di vista, i servizi applicativi sono un'interfaccia per il tuo dominio .

Gli articoli di Jeffrey Palermo sull'architettura delle cipolle ( prima , seconda e terza parte ) sono una buona lettura. Tratta i servizi applicativi come concetti a livello di applicazione , come la sessione di un utente. Sebbene questo sia più vicino alla mia comprensione dei servizi applicativi, non è ancora in linea con i miei pensieri sull'argomento.

I miei pensieri

Ho pensato ai servizi applicativi come alle dipendenze fornite dall'applicazione . In questo caso l'applicazione potrebbe essere un'applicazione desktop o un servizio WCF.

Dominio

Tempo per un esempio. Inizi con il tuo dominio. Tutte le entità e tutti i servizi di dominio che non dipendono da risorse esterne sono implementati qui. Tutti i concetti di dominio che dipendono da risorse esterne sono definiti da un'interfaccia. Ecco un possibile layout di soluzione (nome del progetto in grassetto):

La mia soluzione
- My.Product.Core (My.Product.dll)
  - DomainServices
      IExchangeRateService
    Prodotto
    ProductFactory
    IProductRepository

Le classi Producte ProductFactorysono state implementate nell'assemblaggio principale. Il IProductRepositoryè qualcosa che probabilmente è supportato da un database. L'implementazione di questo non è una preoccupazione del dominio ed è quindi definita da un'interfaccia.

Per ora, ci concentreremo sul IExchangeRateService. La logica aziendale per questo servizio è implementata da un servizio Web esterno. Tuttavia, il suo concetto è ancora parte del dominio ed è rappresentato da questa interfaccia.

Infrastruttura

L'implementazione delle dipendenze esterne fa parte dell'infrastruttura dell'applicazione:

La mia soluzione
+ My.Product.Core (My.Product.dll)
- My.Product.Infrastructure (My.Product.Infrastructure.dll)
  - DomainServices
      XEExchangeRateService
    SqlServerProductRepository

XEExchangeRateServiceimplementa il IExchangeRateServiceservizio di dominio comunicando con xe.com . Questa implementazione può essere utilizzata dalle tue applicazioni che utilizzano il tuo modello di dominio, includendo l'assemblaggio dell'infrastruttura.

Applicazione

Nota che non ho ancora menzionato i servizi applicativi. Vedremo quelli ora. Supponiamo di voler fornire IExchangeRateServiceun'implementazione che utilizza una cache per ricerche rapide. Lo schema di questa classe di decoratori potrebbe apparire così.

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

Notare il ICacheparametro? Questo concetto non fa parte del nostro dominio, quindi non è un servizio di dominio. È un servizio applicativo . È una dipendenza della nostra infrastruttura che può essere fornita dall'applicazione. Introduciamo un'applicazione che lo dimostra:

La mia soluzione
- My.Product.Core (My.Product.dll)
  - DomainServices
      IExchangeRateService
    Prodotto
    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

Tutto questo si riunisce nell'applicazione in questo modo:

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

Sommario

Un'applicazione completa è composta da tre livelli principali:

  • dominio
  • infrastruttura
  • applicazione

Il livello di dominio contiene le entità di dominio e i servizi di dominio autonomi. Tutti i concetti di dominio (inclusi servizi di dominio, ma anche repository) che dipendono da risorse esterne, sono definiti da interfacce.

Il livello di infrastruttura contiene l'implementazione delle interfacce dal livello di dominio. Queste implementazioni possono introdurre nuove dipendenze non di dominio che devono essere fornite all'applicazione. Questi sono i servizi dell'applicazione e sono rappresentati da interfacce.

Il livello dell'applicazione contiene l'implementazione dei servizi dell'applicazione. Il livello applicazione può contenere anche implementazioni aggiuntive di interfacce di dominio, se le implementazioni fornite dal livello infrastruttura non sono sufficienti.

Sebbene questa prospettiva potrebbe non corrispondere alla definizione generale di servizi DDD, separa il dominio dall'applicazione e consente di condividere l'assemblaggio del dominio (e dell'infrastruttura) tra diverse applicazioni.


2
@ dario-g: dovresti ricostruire / ripopolare il tuo modello di dominio dal modello di richiesta e passare il modello di dominio al servizio di dominio. Questa domanda potrebbe fornirti alcune idee. In caso contrario, fammi sapere e vedrò se avrò del tempo per aggiungere una risposta all'altra domanda.
Niels van der Rest

1
@Tiendq: intendi l' IExchangeRateServiceinterfaccia? Questo è un concetto di dominio, ovvero qualcosa che è incluso nel linguaggio onnipresente del cliente. Altre parti del tuo dominio possono dipendere da questo servizio, motivo per cui la sua interfaccia è definita nel livello del dominio. Ma poiché la sua implementazione implica un servizio Web esterno, la classe di implementazione risiede nel livello dell'infrastruttura. In questo modo il livello di dominio riguarda solo la logica aziendale.
Niels van der Rest

4
@Tiendq: in un'architettura a strati tradizionale, l'infrastruttura è generalmente indipendente dal dominio. Ma nell'architettura delle cipolle (vedi link nella mia risposta) l'infrastruttura implementa le dipendenze esterne del dominio. Ma non direi che l'infrastruttura dipende dal dominio, fa solo riferimento . Ho preso il termine "infrastruttura" da Onion Architecture, ma "esterni" potrebbe essere un nome migliore.
Niels van der Rest,

1
@Derek: una di quelle 'cose' potrebbe essere ExchangeRateun'istanza, che contiene una valuta di base, una contro valuta e il valore del tasso di cambio tra queste due valute. Questi valori strettamente correlati rappresentano il concetto di "tasso di cambio" del dominio, quindi vivono nel livello del dominio. Sebbene possa sembrare un semplice DTO, in DDD è chiamato un oggetto valore e potrebbe contenere una logica aziendale aggiuntiva per confrontare o trasformare istanze.
Niels van der Rest,

6
Non sono d'accordo con la parte in cui non sei d'accordo con Vijay ed ecco perché. CachingExchangeRateService è un problema di infrastruttura. Anche se stai genericamente accettando un ICache, l'implementazione di tale ICache dipende dalla tecnologia coinvolta (ad es. Web, Windows). Solo perché è generico non lo rende un servizio applicativo. Un servizio applicativo è l'API del tuo dominio. E se volessi rivelare il tuo dominio a qualcun altro che scrive un'app, cosa useranno? Servizi applicativi e potrebbero non aver bisogno della memorizzazione nella cache, quindi il tuo impianto di memorizzazione nella cache è inutile per loro (ad es. Perché è un'infrastruttura)
Aaron Hawkins,

38

La migliore risorsa che mi ha aiutato a capire la differenza tra un servizio di applicazione e un servizio di dominio è stata l'implementazione java dell'esempio di carico di Eric Evans, trovato qui . Se lo scarichi, puoi controllare gli interni di RoutingService (un servizio di dominio) e BookingService, CargoInspectionService (che sono i servizi applicativi).

Il mio momento "aha" è stato innescato da due cose:

  • Leggendo la descrizione dei Servizi nel link sopra, più precisamente questa frase:

    I servizi di dominio sono espressi in termini di linguaggio onnipresente e tipi di dominio, ovvero gli argomenti del metodo e i valori di ritorno sono classi di dominio appropriate.

  • Leggendo questo post sul blog , in particolare questa parte:

    Quello che trovo di grande aiuto nel separare le mele dalle arance è pensare in termini di flusso di lavoro dell'applicazione. Tutte le logiche relative al flusso di lavoro dell'applicazione finiscono in genere per essere servizi applicativi fatturati nel livello applicazione, mentre i concetti del dominio che non sembrano adattarsi come oggetti modello finiscono per formare uno o più servizi di dominio.


3
Sono d'accordo, questo è esattamente il modo in cui definisco i servizi applicativi e si adatta a tutte le situazioni che ho incontrato finora. I servizi di dominio gestiscono tutto ciò che riguarda gli oggetti di dominio, ma che vanno oltre l'ambito di una singola entità. Esempio: BookReferencesService.GetNextAvailableUniqueTrackingNumber (), l'attenzione è chiaramente alle regole aziendali *. Per quanto riguarda il servizio applicativo, è esattamente ciò che descrivi, la maggior parte delle volte comincio inserendo questo flusso di lavoro aziendale nelle azioni del mio controller e, quando lo noto, rifletto questa logica nel livello di servizio dell'applicazione. Potremmo dire che questo strato è per casi d'uso
tobiak777,

1
* E tali interfacce di servizio di dominio sono utilizzate dalle entità di dominio.
tobiak777,

32

Il servizio di dominio è l'estensione del dominio. Dovrebbe essere visto solo nel contesto del dominio. Non si tratta di un'azione dell'utente come ad esempio la chiusura dell'account o qualcosa del genere. Il servizio di dominio si adatta dove non c'è stato. Altrimenti sarebbe un oggetto di dominio. Il servizio di dominio fa qualcosa che ha senso solo quando viene fatto con altri collaboratori (oggetti di dominio o altri servizi). E quel senso ha la responsabilità di un altro livello.

Il servizio applicativo è quel livello che inizializza e sovrintende all'interazione tra oggetti e servizi di dominio. Il flusso è generalmente simile a questo: recupera l'oggetto (o gli oggetti) di dominio dal repository, esegue un'azione e la rimette (loro) lì (o meno). Può fare di più, ad esempio può verificare l'esistenza di un oggetto di dominio e generare eccezioni di conseguenza. Quindi consente all'utente di interagire con l'applicazione (e questo è probabilmente da dove proviene il suo nome) - manipolando oggetti e servizi di dominio. I servizi applicativi dovrebbero generalmente rappresentare tutti i possibili casi d'uso. Probabilmente la cosa migliore che puoi fare prima di pensare al dominio è creare interfacce di servizi applicativi che ti daranno una visione molto migliore di ciò che stai davvero cercando di fare. Avere tali conoscenze ti consente di concentrarti sul dominio.

In genere i repository possono essere iniettati nei servizi di dominio, ma questo è uno scenario piuttosto raro. Tuttavia, è il livello dell'applicazione che lo fa la maggior parte delle volte.


10
"Il servizio di dominio si adatta dove non c'è stato. Altrimenti sarebbe un oggetto di dominio." fatto clic per me. Grazie.
Nick,

32

Dal libro rosso (Implementing Domain Driven Design, di Vaughn Vernon), ecco come comprendo i concetti:

Gli oggetti di dominio ( entità e oggetti valore ) incapsulano il comportamento richiesto dal (sotto) dominio, rendendolo naturale, espressivo e comprensibile.

I servizi di dominio incapsulano tali comportamenti che non rientrano in un singolo oggetto dominio. Ad esempio, una libreria di libri che presta a Booka Client(con le corrispondenti Inventorymodifiche) potrebbe farlo da un servizio di dominio.

I servizi applicativi gestiscono il flusso di casi d'uso, inclusi eventuali dubbi aggiuntivi necessari in aggiunta al dominio. Espone spesso tali metodi attraverso la sua API, per il consumo da parte di client esterni. Per sfruttare il nostro esempio precedente, il nostro servizio applicativo potrebbe esporre un metodo LendBookToClient(Guid bookGuid, Guid clientGuid)che:

  • Recupera il file Client.
  • Conferma le sue autorizzazioni. ( Nota come abbiamo mantenuto il nostro modello di dominio libero da problemi di sicurezza / gestione degli utenti. Tale inquinamento potrebbe portare a molti problemi. Invece, soddisfiamo questo requisito tecnico qui, nel nostro servizio di applicazione. )
  • Recupera il file Book.
  • Chiama il servizio di dominio (passando il Cliente Book) per gestire la logica di dominio effettiva del prestito del libro al client. Ad esempio, immagino che confermare la disponibilità del libro faccia sicuramente parte della logica del dominio.

Un servizio applicativo dovrebbe generalmente avere un flusso molto semplice. Flussi di servizi applicativi complessi spesso indicano che la logica di dominio è fuoriuscita dal dominio.

Come si spera, il modello di dominio rimane molto pulito in questo modo, ed è facile da capire e discutere con gli esperti del dominio, poiché contiene solo le proprie preoccupazioni aziendali. Il flusso applicativo , d'altra parte, è anche molto più facile da gestire, in quanto è sollevato delle preoccupazioni di dominio e diventa conciso e semplice.


3
Direi che il servizio applicativo è anche il punto in cui vengono risolte le dipendenze. Il suo metodo è un caso d'uso, un singolo flusso, quindi può prendere decisioni informate su implementazioni concrete da usare. Le transazioni del database si adattano anche qui.
Timo,

10

Servizi di dominio: i metodi che non rientrano realmente in una singola entità o che richiedono l'accesso al repository sono contenuti nei servizi di dominio. Il livello del servizio di dominio può anche contenere una logica di dominio propria ed è parte del modello di dominio tanto quanto le entità e gli oggetti valore.

Servizi dell'applicazione: il servizio dell'applicazione è un livello sottile che si trova sopra il modello di dominio e coordina l'attività dell'applicazione. Non contiene la logica aziendale e non detiene lo stato di alcuna entità; tuttavia, può archiviare lo stato di una transazione del flusso di lavoro aziendale. Si utilizza un servizio applicazione per fornire un'API nel modello di dominio utilizzando il modello di messaggistica di richiesta-risposta.

Millett, C (2010). Modelli di progettazione ASP.NET professionali. Wiley Publishing. 92.


7

Servizi di dominio : un servizio che esprime una logica aziendale che non fa parte di alcun Radice aggregata.

  • Hai 2 aggregati:

    • Product che contiene nome e prezzo.
    • Purchase che contiene la data di acquisto, l'elenco dei prodotti ordinati con quantità e prezzo del prodotto in quel momento e il metodo di pagamento.
  • Checkout non fa parte di nessuno di questi due modelli ed è un concetto nella tua attività.

  • Checkoutpuò essere creato come un servizio di dominio che recupera tutto il prodotto e calcola il prezzo totale, paga il totale chiamando un altro servizio di dominio PaymentServicecon una parte di implementazione dell'infrastruttura e convertendolo in Purchase.

Servizi applicativi : un servizio che "orchestra" o esercita metodi di dominio. Questo può essere semplice come solo il tuo controller.

Questo è il posto dove di solito fai:

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

Puoi fare delle convalide qui come verificare se un Productè unico. A meno che un Productessere univoco sia un invariante, ciò dovrebbe far parte del servizio di dominio che potrebbe essere chiamato UniqueProductCheckerperché non può essere parte della Productclasse e interagire con più aggregati.

Ecco un esempio completo del progetto DDD: https://github.com/VaughnVernon/IDDD_Samples

È possibile trovare molti esempi di servizi applicativi e un paio di servizi di dominio


È obbligatorio convalidare e salvare entità solo nei Servizi applicazione? Se ho entità A, B e C e tutte correlate tra loro (A -> B -> C) e l'operazione su A dovrebbe causare modifiche a B e C chiamando un servizio di dominio da un altro, come si fa?
MrNVK,

> È obbligatorio convalidare e salvare entità solo in Servizi applicazioni? Se devi, allora sì. La maggior parte delle volte devi controllare se esiste un ID perché altrimenti lavorerai su una variabile nulla.
importa il

1
> Se ho entità A, B e C e tutte collegate tra loro (A -> B -> C) e l'operazione su A dovrebbe causare modifiche a B e C chiamando un servizio di dominio da un altro, come farlo ? Non sono sicuro di cosa intendi per "chiamare un servizio di dominio da un altro", ma per reazioni ai cambiamenti di un'entità, puoi utilizzare Eventi o orchestrarlo semplicemente con un servizio applicazione come: aggregateA.doOperation (), aggregateB.doAnother ( ). Cerca: Orchestration vs Choreography
doesnotmatter il

Grazie per la risposta! "chiamando un servizio di dominio da un altro" - Voglio dire, se ho un'operazione complessa sull'entità A, allora devo usare ADomainService. Ma questa operazione, oltre all'entità A, influisce sull'entità B. Anche l'operazione che deve essere eseguita sull'entità B in ADomainService è complessa. Quindi devo usare BDomainService da ADomainService. Ora dubito di questo approccio :) Ma se inserissi questa logica in ApplicationService, non spezzerebbe l'incapsulamento dei processi aziendali che dovrebbero essere solo a livello di dominio, non a livello di applicazione?
MrNVK,

Puoi semplicemente emettere un evento dal tuo servizio di dominio se pensi che dovrebbe essere in un servizio di dominio anziché in un servizio applicazione.
importa il

1

Pensa a un servizio di dominio come a un oggetto che implementa la logica di business o la logica relativa alle regole di business sugli oggetti di dominio e questa logica è difficile da adattare agli stessi oggetti di dominio e inoltre non causa il cambio di stato del servizio di dominio (il servizio di dominio è un oggetto senza uno "stato" o meglio senza uno stato che abbia un significato commerciale) ma alla fine cambia lo stato solo degli oggetti di dominio su cui opera.

Mentre un servizio applicativo implementa la logica del livello applicativo come interazione dell'utente, convalida dell'input, logica non correlata al business ma ad altre preoccupazioni: autenticazione, sicurezza, invio di e-mail e così via ..., limitandosi a utilizzare semplicemente i servizi esposti dagli oggetti di dominio.

Un esempio di questo potrebbe essere il seguente scenario pensato solo a scopo esplicativo: dobbiamo implementare una piccolissima app di utilità domotica che esegue una semplice operazione, ovvero "accendere le luci, quando qualcuno apre la porta della stanza di una casa per entrare entrare e spegnere la luce quando si chiude la porta che esce dalla stanza ".

Semplificando molto consideriamo solo 2 entità di dominio: Doore Lampognuna di esse ha 2 stati, rispettivamente open/closede on/offe metodi specifici per operare cambiamenti di stato su di essi.

In questo caso abbiamo bisogno di un servizio di dominio che esegua l'operazione specifica di accendere la luce quando qualcuno apre la porta dall'esterno per entrare in una stanza, perché la porta e gli oggetti lampada non possono implementare questa logica in un modo che consideriamo adatto alla loro natura .

Possiamo chiamare il nostro servizio di dominio come DomoticDomainServicee implementare 2 metodi: OpenTheDoorAndTurnOnTheLighte CloseTheDoorAndTurnOffTheLight, questi 2 metodi cambiano notevolmente lo stato di entrambi gli oggetti Doore Lampin open/one closed/off.

Lo stato di entrata o uscita da una stanza che non è presente nell'oggetto del servizio di dominio e negli oggetti del dominio, ma sarà implementato come semplice interazione dell'utente da un servizio dell'applicazione, che possiamo chiamare HouseService, che implementa alcuni gestori di eventi come onOpenRoom1DoorToEntere onCloseRoom1DoorToExitcosì via per ogni stanza (questo è solo un esempio per spiegare lo scopo ..) , che riguarderanno rispettivamente i metodi di servizio del dominio di chiamata per eseguire il comportamento assistito (non abbiamo considerato l'entità Roomperché è solo un esempio) .

Questo esempio, lungi dall'essere un'applicazione ben progettata nel mondo reale, ha il solo scopo (come più volte detto) di spiegare cos'è un servizio di dominio e la sua differenza da un servizio di applicazione, spero che sia chiaro e utile.


Ciro: Il tuo esempio non è pratico ed è molto confuso.
Morteza Azizi,

Ciao Morteza, potresti essere più specifico? Il tuo rischio è di essere solo un "giudizio" senza alcun vero argomento. Grazie
Ciro Corvino il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.