Come implementare il modello di dominio con il repository senza perdite di responsabilità?

1

Immagina, abbiamo moduli di test con un gran numero di domande e possiamo creare test che conterranno esattamente 30 domande.

public class Module
{
    public long Id { get; set; }

    public List<Question> Questions { get; set; } // all questions
}

public class Test 
{
    public long Id { get; set; }

    public List<Question> Questions { get; set; } // exactly 30 per attempt
}

Questo è quello che farei di solito:

public class QuestionsRepository
{
    public Question[] GetRandomQuestions(int count)
    {
        return context.Set<Question>()
            .Where(x => x.Status == QuestionStatus.Active)
            .OrderBy(x => Guid.NewGuid())
            .Take(count)
            .ToArray();
    }
}

public class TestService
{
    public Test StartNewTest()
    {
        // take random 30 and build a new test
        return new Test
        {
            Questions = questionsRepository.GetRandomQuestions(30)
        };
    }
}

Tuttavia, ciò significa che disponiamo di modelli di dominio anemici e che la logica del dominio sta filtrando nel repository, sa quale stato deve essere attivo, che prendiamo solo un numero limitato di domande ecc.

Questo è quello che mi aspetto che DDD assomigli:

public class Module
{
    public long Id { get; set; }

    public List<Question> Questions { get; set; } // all questions

    public Test StartNewTest(int questionsCount) 
    {
        return new Test
        {
            Questions = Questions
                .OrderBy(x => Guid.NewGuid())
                .Take(questionsCount)
                .ToArray();
        };
    }
}

public class TestService
{
    public Test StartNewTest()
    {
        var module = moduleRepository.GetDefaultModule();
        return module.StartNewTest();
    }
}    

Ora, la logica di dominio è contenuta in Modelli di dominio. Tuttavia, questo significa che tutte le domande verranno caricate in memoria con impazienza e quindi verranno filtrate come IEnumerable che potrebbe essere molto inefficiente se avessimo un numero di domande elevato.

Come si implementa il modello di dominio ignorante alla persistenza rendendo tutte le query SQL efficienti e ottimali? Non riesco a capire come progettare DDD senza perdite di logica di dominio nei repository.

    
posta Yeldar Kurmangaliyev 05.05.2018 - 14:22
fonte

2 risposte

3

How do you implement persistence-ignorant Domain Model while making all SQL queries efficient and optimal? I cannot understand how to design DDD without leaking domain logic into repositories.

Per iniziare, rivedi la tua comprensione di "repository"; ecco cosa ha scritto originariamente Evans:

create an object that can provide the illusion of an in-memory collection of all objects of that type.

La motivazione è che i dettagli di implementazione di come la raccolta è effettivamente archiviata sono nascosti dietro un'interfaccia agnostica di implementazione. Vedi Parnas 1971 .

Ecco il trucco più importante: l'interfaccia stessa non deve essere dominio agnostica.

Un'interpretazione del modello di repository è che l'interfaccia descrive un contratto tra i dettagli di implementazione della collezione e i consumatori. Come spiega Greg Young , ci sono dei vantaggi nel rendere i dettagli di quel contratto esplicito. L'interfaccia documenta le funzionalità che l'implementazione deve fornire.

Ora, sono d'accordo sul fatto che abbiate ragione - nei casi più comuni, non dovreste aver bisogno di un'implementazione di repository che capisca un concetto di dominio come "attivo". Quello che probabilmente vuoi è che il modello di dominio possa scegliere cosa significa attivo, e lasciare che il repository capisca come implementare la query.

Quindi l'interfaccia del tuo repository potrebbe essere simile a

Repository {
    Question [] getRandomQuestions(Status s, int count);
}

Non sarebbe sbagliato avvolgere il repository in un servizio di dominio con un'API più esplicita.

Active Questions {
    Question [] getRandom(int count) {
        return questionRepository.get(QuestionStatus.Active, count);
    }
}

That is what I expect DDD to look like...

I pattern di creazione sono strani.

public Test StartNewTest()
{
    var module = moduleRepository.GetDefaultModule();
    return module.StartNewTest();
}

Una delle cose importanti da notare qui è che l'avvio di un nuovo test non modifica il modulo ; succede solo che il modulo sia responsabile dello stato che vuoi copiare.

è uno schema che segue questa osservazione: legge e scrive hanno requisiti diversi, quindi forse dovrebbero avere percorsi di codice diversi. Qui, una conseguenza di questa idea potrebbe essere che non si creano test dai moduli, ma piuttosto da una visione più semplice del modello. Il vantaggio è che puoi utilizzare una vista caricata pigra in quelle circostanze in cui non è necessario caricare l'intero modulo.

    
risposta data 05.05.2018 - 18:05
fonte
0

IEnumerable / IQueryable non verrà eseguito fino a quando non invochi esplicitamente la raccolta

Per evitare di memorizzare milioni di domande in memoria, utilizzare un database

Può esaminare una libreria modello relazionale dell'oggetto come Entity Framework

    
risposta data 05.05.2018 - 16:21
fonte