Entità dati, entità dominio e repository

3

Sto cercando di capire come funziona DDD ma sono bloccato. Ecco come ho impostato il mio progetto:

Data Access Layer
 -Entity models that map to the db
 -Db connection stuff
 -Repositories implementations

Domain Layer
 -Models that represent the DAL entity models
 -Repositories interfaces

Application Layer
 -MVC application that uses Domain models

Il primo problema che vedo qui è che i modelli di dominio sono esattamente gli stessi dei modelli di entità e ho un problema serio con questo: i modelli di entità hanno ovviamente la convalida configurata in essi, cose come "lunghezza massima", " nullable "," required ", ecc. Ora, per conformarmi a ciò che capisco è DDD, non posso usare direttamente questi modelli da nessuna parte, ad eccezione del DAL, quindi ho creato il mio livello di dominio. Nel livello del dominio, ho tutte queste regole di convalida duplicate per la convalida dell'interfaccia utente, e ciò che è ancora peggio è che se devo cambiare una regola, dovrò cambiarla in due punti: il DAL e il dominio.

Esempio:

User Entity in DAL
Name (required)
Last name (required)
Email (required, maxlen 120)
Username (required, maxlen 120)

User Domain Model
Name (required)
Last name (required)
Email (required, maxlen 120)
Username (required, maxlen 120)

Un'altra cosa che trovo molto strana è l'organizzazione dei repository in questa architettura. Seguendo ciò che ho letto, ho creato un'interfaccia GenericRepository e un'interfaccia UserRepository, che eredita il GenericRepository, il tutto nel livello Domain. Ho implementato il GenericRepository nel DAL e l'implementazione crea un DAO per il tipo di entità utilizzata per creare il repository. Fin qui, tutto bene.

Quindi, ho implementato il UserRepository, e qui ho un altro problema: l'interfaccia UserRepository si aspetta il modello Domain User e quando cerco di implementare l'interfaccia nel DAL, devo implementarlo usando il modello Domain User , che causa la creazione del DAO per un modello di dominio, non un modello DAL, e questo non ha alcun senso. L'unico da risolvere sarebbe quello di fare riferimento al DAL nel livello Dominio, che è sbagliato.

Domain Layer:

public interface IGenericRepository<TEntity>
{
    TEntity FindById(TKey id);
}

public interface IUserRepository : IGenericRepository<Domain.User>
{
    Task<User> FindByUserNameAsync(string userName);
}


DAL:

public abstract class GenericRepository<TEntity> : IGenericRepository<TEntity>
{
    protected DbContext ctx;
    protected DAO<Entity> dao;

    public GenericRepository(DbContext context)
    {
        ctx = context;
        dao= ctx.Dao<TEntity>();
    }

    public virtual TEntity FindById(TKey id)
    {
        return dao.Find(id);
    }
}

 public class UserRepository : GenericRepository<Domain.Models.User>, IUserRepository
{
    public UserRepository(DbContext context)
        : base(context)
    {
       // THIS WILL CREATE A DAO FOR A DOMAIN MODEL
    }

    // rest of code...
}

Qualcuno può far luce su ciò che mi manca nel DDD?

    
posta victor 23.02.2018 - 20:52
fonte

3 risposte

4

Va bene. C'è molto da decomprimere qui, ma penso che la causa principale di una parte della tua confusione derivi dall'iniziare questo processo con il modello fisico in prima linea. Questo di solito causa tutti i tipi di problemi per le persone che cercano per la prima volta di implementare DDD. L'obiettivo di DDD è quello di modellare il comportamento di un sistema tale che il risultato sia un'astrazione utile dei requisiti di funzione del dominio principale. Per ora, dimentica il tuo DAL. È un dettaglio di implementazione.

Inizia modellando il tuo sistema organizzando i modelli con comportamento in contesti limitati. È il comportamento dei modelli che li collega veramente. I dati / attributi che le entità contengono raramente sono un buon punto di partenza per modellare i requisiti funzionali di un sistema complesso. Mi piacerebbe fornire alcuni esempi, ma non hai specificato il tuo dominio, quindi ti fornirò solo alcuni suggerimenti:

A partire dal tuo modello di dominio User . Scommetterei sul sink di cucina che l'unica ragione per cui esiste è che hai un'entità User . Il problema con questo è che User implica poco o nessun comportamento (usa cosa?), Probabilmente comprende troppa conoscenza ed è quindi troppo astratto. Cosa fanno i tuoi utenti (autenticazione a parte)? Negozio? %codice%. Commento? Shopper / Commenter . Vendere? %codice%. La parte importante è che questi non sono mutuamente esclusivi! Un Poster può essere tutte queste cose a seconda del comportamento in cui si impegneranno. Il fatto che tutti si associno alla stessa tabella di database è un dettaglio di implementazione.

Vedi dove vado? Potresti avere un contesto Shopping che ha Seller , User e Shopper e un contesto di fatturazione con ShoppingCart , CartItem e Buyer dove i modelli di ciascuna mappa rispettivamente a PurchaseRequest , LineItem e [User] tabella del database. Il tuo modello dovrebbe essere l'obiettivo assoluto di questo processo. Non come è persistito.

Per quanto riguarda una risposta diretta alla tua domanda. Ci sono un numero di oggetti comuni trovati in un dominio. [Order] e [OrderItem] sono i capisaldi del tuo modello, ma spesso troverai DomainModels e ValueObjects che giocano un ruolo di supporto. Poiché gli archivi e le fabbriche hanno quasi sempre bisogno di conoscere i dettagli di implementazione del tuo dominio, di solito fanno parte del tuo modello di dominio (ma non fanno parte del diagramma, ad esempio).

    
risposta data 23.02.2018 - 22:37
fonte
1

Sembra che tu abbia visto qualcuno che implementa un design DDD usando l'architettura che hai menzionato nella domanda, ma non è quello che è DDD. Questa è solo una delle molte possibili implementazioni. Ti consiglio di seguire la risposta della diapositiva sul lato sinistro relativa al DDD.

Per quanto riguarda la tua domanda, la vedo più come una domanda generale di Entity Framework e di progetti. E qui ci sono alcuni commenti:

I tuoi modelli di dominio non rappresentano i tuoi modelli DAL. È il contrario. Devi progettare i tuoi modelli di dominio per rappresentare alcuni concetti nel tuo dominio. Quindi il tuo DAL è solo un'utilità per mantenere questi modelli. Personalmente, non uso affatto le entità DAL. EF può mappare direttamente i tuoi modelli di dominio. Utilizza i file di mappatura con EntityTypeConfiguration anziché gli attributi, che contaminano il tuo modello o la tua fluente API, che è molto prolisso e disordinato.

Non creare un'interfaccia repository generica. Ciò porterà ad avere repository con metodi che non dovrebbero esserci. Ogni repository dovrebbe avere solo i metodi necessari in base alle proprie esigenze.

Definisci le interfacce del repository nel tuo Business Layer. È il tuo livello aziendale a dettare ciò che un repository deve implementare, non il contrario. Quindi è possibile avere uno o più progetti che implementano questi repository, facendo riferimento ai progetti del livello aziendale, in modo che possano mappare il modello e implementare le interfacce.

Non utilizzare il tuo modello di dominio dal tuo livello di interfaccia utente. Il tuo Business Layer dovrebbe fornire interfacce con operazioni di scrittura (comandi), che accettano input DTO e con operazioni di lettura (query), che restituiscono DTO.

Definisci queste interfacce e DTO in un progetto separato di proprietà del tuo Business Layer. Poi il tuo livello UI oi web api controllers possono fare riferimento a quel progetto e consumare le interfacce.

Se il tuo livello aziendale ha bisogno di accedere a servizi esterni, definisci le interfacce per loro nel livello aziendale, con i metodi esatti di cui hai bisogno da quei servizi. Quindi puoi creare progetti che implementano queste interfacce. È esattamente lo stesso concetto dei repository.

    
risposta data 26.02.2018 - 07:59
fonte
0

(disclaimer, non sto forzando o proponendo questa come una risposta corretta una sola volta alla tua domanda, questo è semplicemente come lo faccio nelle mie soluzioni, sulla base del Architettura delle cipolle .)

Credo che la tua principale confusione derivi dalla duplicazione di oggetti o modelli di dominio nel livello DAL. Penso sempre all'oggetto Dominio come a un'entità nel mio database di back-end [* 1] . Per me questa è la parte principale delle informazioni di cui ho bisogno per mantenere la soluzione. Quindi non c'è bisogno di replicare il livello DAL.

Ecco come strutturo la cartella della mia soluzione (basata sul tuo input):

  1. Domain Layer

    • Modelli che rappresentano gli oggetti del database DAL entity (tabelle e viste).

    • Repository interfacce .

  2. Livello di accesso ai dati

    • elementi di connessione Db.

    • Archivi implementazioni .

    • Configurazione dei modelli (configurazione fluente).

  3. Livello infrastruttura (aziendale)

    • Servizi esterni.

    • Utilità.

    • Servizi specifici utilizzati successivamente nel livello front-end.

  4. UI Applicazione Layer

    • Progetto WebAPI, a volte nella propria cartella per una separazione più chiara [* 2] .

    • Applicazione MVC / angolare / reagisci / etc che utilizza Modelli di dominio tutti sopra i livelli come in Onion-architecture.

Ogni livello, dall'alto verso il basso, fa riferimento a tutti i livelli precedenti. Quindi nel livello 3. (Infrastruttura) faccio riferimento ai progetti del dominio e del livello DAL e così via.

[* 1] Di solito includo anche qualsiasi vista del database tra oggetti Domain, questo elimina in gran parte la necessità di un "UserService" solo per creare aggregati per il livello front-end.

[* 2] A seconda della dimensione / ambito della soluzione, ometto il progetto WebAPI e consumo semplicemente tutti i layer precedenti direttamente nell'app MVC. Se tuttavia utilizzo WebAPI, l'app MVC utilizza solo questa WebAPI e, in alcuni rari casi, fa riferimento, ad esempio, ai servizi esterni del livello Infrastruttura.

Modifica come da commento:

Il livello Infrastruttura di solito ma non necessariamente ha questi tre progetti:

  1. Servizi esterni - supponiamo che la mia applicazione abbia bisogno di dati da un servizio DMV pubblico. Questo è il posto in cui implementarlo. Aggiungere un riferimento al servizio, aggiungere la logica per chiamare il servizio DMV, preparare gli oggetti DTO di richiesta e risposta che vengono poi utilizzati per comunicare con WebAPI o direttamente all'applicazione MVC.

  2. Utility - questo è un progetto abbastanza grande di molte estensioni o metodi statici come ad esempio "StringToDateTime" o "IsValidTaxNumber" ecc. Questo ci sforziamo di liberarci dal fare riferimento ad altri progetti. Credo che l'unica dipendenza attualmente presente sia Newtonsoft Json utilizzata nella sezione Serializzazione.

  3. Servizi specifici - questo è principalmente usato come middleware per impedire il gonfiamento dei miei controller (API o MVC). Se ho un cliente di controller e ci sono alcune logiche di business, lo inserirò in questo progetto. In questo modo ho una visione chiara della presentazione e dei livelli di business / logici.

risposta data 25.02.2018 - 17:52
fonte

Leggi altre domande sui tag