Separazione di oggetti di dominio e modelli di dati

3

Ho esaminato molti articoli, blog e argomenti SO relativi alla separazione dell'oggetto dominio e dei modelli di dati. Quasi ogni risposta diceva: dovresti avere classi separate per il dominio e la persistenza dei dati, perché ...

Capisco perché è buono, ma non ho trovato nemmeno uno pseudo esempio per risolvere questo problema, e ho dei suggerimenti: forse tutti sanno che dovrebbe essere implementato in questo modo, ma nessuno lo fa?

Nelle mie soluzioni, sto usando Automapper per raggiungere questo obiettivo (mappando il dominio al modello DB e il dominio ai DTO), ma vorrei confrontare il mio approccio con qualsiasi altro, ma come ho detto non riesco a trovarlo buon esempio.

Forse ci sono solo parole sulle buone pratiche che non esistono in applicazioni reali e funzionanti, ed è più facile usare solo le classi di dominio come modelli di dati usati dal modulo, che possono dare molti vantaggi come il caching o il tracciamento delle modifiche?

Attualmente sto lavorando su un'applicazione, dove i dati sono memorizzati nel vecchio database, quale schema ... fa molto schifo, quindi usare i modelli DB come modelli di dominio, in questo caso, fa sì che anche il mio dominio faccia schifo.

Cosa ne pensi di questo?

Funzioni ORM e amp; processo di sviluppo più veloce > separando i modelli di dominio e dati e amp; & processo di sviluppo più lento?

    
posta bielu000 25.09.2018 - 08:39
fonte

2 risposte

1

Non scrivere un'altra copia del tuo modello di dati nel codice C # quando hai già quel modello di dati nel database. Quale sarebbe il punto?

Focalizza l'attenzione sul problema reale, ovvero il mapping da tale modello dati nel DB al modello di dominio.

Gli ORM lo fanno.

Quindi l'unica ragione per non usare un ORM sarebbe che tu abbia un modo ancora migliore di fare il mapping.

Il che non è impossibile: il Dapper "micro-ORM" è molto popolare: è abbastanza buono da alimentare StackOverflow ed è eccellente per molti casi d'uso.

Se si procede con la rotta micro-ORM (o ADO, o SQL raw), è probabile che si finisca per creare classi DTO per contenere i dati che si ottengono dal db. Ciò non significa però che stai cercando di costruire un modello. (E sì, per la mappatura da e verso DTO, AutoMapper è apparso nella maggior parte dei posti in cui ho lavorato negli ultimi anni. Trovo che gli errori nel codice di automapper siano davvero dolorosi da rintracciare, ma forse perché è facile da usare e l'abuso).

Sì a un modello di dati separato, ma ne hai già uno . Quindi sì a un ORM se questo è il modo più economico / più efficace per fare la mappatura.

Non so quali articoli hai letto, ma ho trovato questo articolo e alcuni dei commenti abbastanza percettivi: enterprisecraftsmanship having-the-domain-model-separate-from-the-persistence-model

    
risposta data 25.09.2018 - 15:02
fonte
0

Ho trovato un pacchetto NuGet, che mi aiuta a fare esattamente quello che hai detto. Sto usando il classico approccio DDD:

  • Dominio - > DataModel = AutoMapper
  • Dominio - > DTO = AutoMapper
  • DataModel - > Dominio = Fabbrica
  • DTO - > Dominio = Fabbrica

A prima vista, sembra un vero e proprio overkill avere 3 modelli, ma ci permette di progettare il dominio senza doverci preoccupare di problemi tecnici, ad esempio dovendo impostare i campi di sola lettura come setter privati, perché ORM non può impostare campi di sola lettura ecc.

L'intero codice del repository può essere trovato qui: link Attualmente, sta usando un MongoDB, ma il pattern è carino praticamente uguale. Un esempio su come usarlo può essere trovato qui: link

Come funziona:

Modello di dominio:

public class Individual : AggregateRoot
{
    public Individual(string id, string firstName, string lastName, DateTime birthdate)
        : base(id)
    {
        Guard.ObjectNotNull(() => firstName);
        Guard.ObjectNotNull(() => lastName);

        FirstName = firstName;
        LastName = lastName;
        Birthdate = birthdate;
    }

    public DateTime Birthdate { get; }
    public string FirstName { get; }
    public string LastName { get; }
}

crea un repository per la radice aggregata, ad esempio:

public class IndividualRepository : RepositoryBase<Individual, IndividualDataModel>
{
    public IndividualRepository(IDataModelRepository<IndividualDataModel> dataModelRepository, IDataModelAdapter<IndividualDataModel, Individual> dataModelAdapter) : base(dataModelRepository, dataModelAdapter)
    {
    }
}

Una datamodel, che rappresenta la "Entity":

public class IndividualDataModel : DataModelBase
{
    public DateTime Birthdate { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

E un DataModelAdapter, che mappa in entrambe le direzioni:

public class IndividualDataModelAdapter : DataModelAdapterBase<IndividualDataModel, Individual>
{
    private readonly IIndividualFactory _individualFactory;

    public IndividualDataModelAdapter(IIndividualFactory individualFactory, IMapper mapper) : base(mapper)
    {
        _individualFactory = individualFactory;
    }

    public override Individual Adapt(IndividualDataModel dataModel)
    {
        return _individualFactory.Create(
            dataModel.FirstName,
            dataModel.LastName,
            dataModel.Birthdate,
            dataModel.Id);
    }
}

Questo è praticamente tutto, l'implementazione del Repository ha accesso al datamodel e ai queryable, e l'Interface funziona solo con i Modelli di Dominio.

Per rispondere alla tua domanda: il codice dell'infrastruttura può essere facilmente reso così generico, che ci sono ritardi minimi. Tutto quello che devi fare è mantenere la possibilità di definire la mappatura e annullarla per esigenze speciali.

    
risposta data 25.09.2018 - 12:57
fonte