DDD: creazione di moduli riutilizzabili e distinzioni del tipo di servizio (dominio, infrastruttura, applicazione)

8

Quindi, dopo aver letto "Implementing Domain-Driven Design by Vaughn Vernon", ho deciso di ridefinire il mio codice per una migliore riutilizzabilità isolando ciò che ritengo essere concetti di dominio centrale in moduli separati.

Ogni modulo contiene il proprio insieme di livelli architettonici distinti che includono il dominio, l'infrastruttura e il livello di applicazione / presentazione (per raccomandazione di Vaughn ho deciso di separare ulteriormente le responsabilità del livello applicazione dalle rotte, controller MVC + modelli che esiste nel livello di presentazione).

Ho deciso di inserire ciascuno di questi livelli all'interno del proprio pacchetto; e ogni pacchetto fa riferimento al livello sottostante come dipendenza. Ad esempio: il livello di presentazione dipende dal livello applicazione, dall'applicazione dipende dall'infrastruttura, ecc. Poiché il repository fa parte del dominio, ogni interfaccia del repository esiste all'interno del livello / pacchetto dominio con l'implementazione come una responsabilità del livello / pacchetto infrastruttura (Doctrine , ecc.)

Spero che, ristrutturando il mio codice in questo modo, riuscirò a sostituire il livello dell'applicazione e riutilizzare il mio dominio su più applicazioni Web.

Sembra che il codice stia iniziando a formarsi di nuovo, tuttavia ciò che ancora mi confonde è questa distinzione tra Application, Infrastructure e Domain Services.

Un esempio comune di servizio di dominio è qualcosa che useresti per le password hash. Questo ha senso per me da una prospettiva SRP in quanto l'entità utente non dovrebbe preoccuparsi dei numerosi algoritmi di hashing che potrebbero essere utilizzati per memorizzare le credenziali di un utente.

Quindi con questo in mente ho trattato questo nuovo servizio di Dominio allo stesso modo dei miei Archivi; definendo un'interfaccia nel dominio e lasciando l'implementazione fino al livello infrastruttura. Tuttavia, ora mi chiedo cosa dovrebbe essere fatto con i servizi di applicazione.

Allo stato attuale, ogni Entità ha il proprio Servizio Applicativo, cioè l'Entità Utente ha un UserService all'interno del Livello Applicazione. In questo caso, UserService è responsabile dell'analisi dei tipi di dati primitivi e della gestione di un caso d'uso comune "UserService :: CreateUser (nome stringa, e-mail stringa, ecc.): Utente.

Ciò che mi preoccupa è il fatto che dovrò implementare nuovamente questa logica su più applicazioni nel caso decidessi di sostituire il livello Application. Quindi immagino che questo mi porti alle mie prossime domande:

  1. I servizi di dominio sono semplicemente un'interfaccia esistente per fornire un livello di astrazione tra il livello dell'infrastruttura e il modello? es .: Repository + HashingServices, ecc.

  2. Ho detto di avere un servizio applicativo che assomiglia a questo:

    • Accesso / Applicazione / Servizi / UserService :: CreateUser (nome stringa, email stringa, ecc.): Utente

    • La firma del metodo accetta argomenti di tipo dati primitivi e restituisce una nuova Entità utente (non un DTO!).

    Questo appartiene al livello Infrastruttura come un'implementazione di alcune interfacce definite all'interno del livello Dominio o il livello di applicazione in effetti è più appropriato a causa di argomenti di tipo di dati primitivi, ecc. ?

    esempio:

    Access/Domain/Services/UserServiceInterface 
    

    e

    Access/Infrastructure/Services/UserService implements UserServiceInterface
    
  3. Come dovrebbero i moduli separati gestire le relazioni unidirezionali. Dovrebbe il livello applicativo del modulo B del modulo di riferimento A (come sta facendo ora) o l'implementazione dell'infrastruttura (tramite interfaccia separata)?

  4. I servizi Application Layer richiedono un'interfaccia separata? Se la risposta è sì, dove dovrebbero essere collocati?

posta user2308097 20.03.2016 - 21:33
fonte

2 risposte

6

Are Domain Services merely an Interface which exist to provide a layer of abstraction between the Infrastructure Layer and your Model? ie: Repositories + HashingServices, etc.

Le responsabilità dei servizi di dominio comprendono diverse cose. La più ovvia è la logica abitativa che non si adatta a una singola entità. Ad esempio, potrebbe essere necessario autorizzare un rimborso per un determinato acquisto, ma per completare l'operazione sono necessari i dati dall'entità Purchase , Customer entità, CustomerMembership entità.

I servizi di dominio forniscono anche le operazioni necessarie al dominio per completare la sua funzionalità come PasswordEncryptionService , ma l'implementazione di questo servizio risiederà nel livello infrastruttura dal momento che sarà per lo più una soluzione tecnica.

I servizi di infrastruttura sono servizi che quindi un'operazione di infrastruttura come l'apertura di una connessione di rete, la copia di file dal file system, la comunicazione con un servizio Web esterno o la conversazione con il database.

I servizi di applicazione sono l'implementazione di un caso d'uso nell'applicazione che stai creando. Se si annulla una prenotazione di un volo, si:

  1. Interrogare il database per l'oggetto Reservation.
  2. invoca Reservation- > cancel.
  3. Salva oggetto in DB.

Il livello applicazione è il client del dominio. Il dominio non ha idea di quale sia il tuo caso d'uso. Espone semplicemente la funzionalità attraverso i suoi aggregati e servizi di dominio. Il livello dell'applicazione tuttavia rispecchia ciò che stai cercando di ottenere orchestrando i livelli di dominio e infrastruttura.

I mentioned having an Application Service which looks like this: Access/Application/Services/UserService::CreateUser(string name, string email, etc): User The method signature accepts primitive data type arguments and returns a new User Entity (not a DTO!).

PHP potrebbe non essere il posto migliore per iniziare a conoscere DDD dato che molti dei framework PHP là fuori (Laravel, Symfony, Zend, ecc.) tendono a promuovere RAD. Si concentrano maggiormente sul CRUD e sulla traduzione di forme in entità. CRUD! = DDD

Il tuo livello di presentazione dovrebbe essere responsabile della lettura degli input del modulo dall'oggetto richiesta e del richiamo del servizio dell'applicazione corretto. Il servizio applicativo creerà l'utente e invocherà il repository utente per salvare il nuovo utente. Opzionalmente è possibile restituire un DTO dell'utente al livello di presentazione.

How should separate modules handle unidirectional relationships. Should module A reference module B's application layer (as it's doing now) or the infrastructure implementation (via separated interface)?

Il modulo di parole nel gergo DDD ha un significato diverso da quello che stai descrivendo. Un modulo dovrebbe contenere concetti correlati. Ad esempio, un modulo ordini nel livello dominio potrebbe ospitare l'aggregato Ordine, l'entità OrderItem, OrderRepositoryInterface e MaxOrderValidationService.

Un modulo Ordine nel livello applicazione può ospitare OrderApplicationServie, CreateOrderCommand e OrderDto.

Se parli di livelli, ogni strato dovrebbe preferibilmente dipendere dalle interfacce di altri livelli, quando possibile. Lo strato di presentazione dovrebbe dipendere dalle interfacce del livello dell'applicazione. Il livello applicazione dovrebbe fare riferimento alle interfacce dei repository o dei servizi di dominio.

Personalmente non creo interfacce per entità e oggetti valore perché credo che le interfacce debbano essere correlate a un comportamento, ma YMMV:)

Do Application Layer Services require a Separate Interface? If the answer is yes then where should they be located?

Dipende :) per applicazioni complesse costruisco interfacce coz applichiamo rigorosi test di unità, integrazione e accettazione. L'accoppiamento lento è fondamentale qui e le interfacce si trovano nello stesso livello (livello applicazione).

Per la semplice app che costruisco direttamente sui servizi dell'app.

    
risposta data 21.03.2016 - 04:24
fonte
1

Risposta breve a una lunga domanda, ma vedo il modello come segue

Regola 1: gli oggetti di dominio dovrebbero avere una singola radice di aggregazione

Regola 2: i root aggregati non dovrebbero essere troppo grandi, dividere le cose in contesti limitati

Problema: i root aggregati diventano presto troppo grandi e non esiste un modo chiaro per tracciare una linea tra i vari modelli di dominio in essi contenuti

Soluzione: servizi di dominio. Crea interfacce che puoi iniettare in Modelli di Dominio che consentono loro di fare cose al di fuori del loro Dominio di Dominio o Radice Aggregata.

Quindi penso che direi che i tuoi esempi sono solo servizi / repository normali, IDatabaseRepositoryForStoringUsers o IGenericHashingCode

Un servizio di dominio consente la comunicazione tra contesti limitati. vale a dire

User.UpgradeAccount(IAccountService accountService)
{
    accountService.UpgradeUsersAccount(this.Id);
}

Dove utenti e account si trovano in percorsi separati / contesti limitati.

Se l'utente e l'account si trovano nella stessa radice di aggregazione, dovresti naturalmente essere in grado di fare:

User.UpgradeAccount()
{
    this.MyAccount.Upgrade();
}

Non sono del tutto chiaro dalla tua domanda su come stai integrando la roba su Application / Infrastructure e il modulo. Se non si desidera realmente alcuna referenza incrociata tra contesti limitati, inserire le proprie Interfacce di servizio dominio nel proprio modulo che non fa riferimento a nessun altro Contesto Limitato. limitandoti a esporre i tipi di valori di base o solo i DTO perhapse

    
risposta data 20.03.2016 - 23:18
fonte