Perché separare la classe CommandHandler con Handle () invece di gestire il metodo nel comando stesso

11

Ho una parte del pattern CQRS implementata usando S # arp Architecture in questo modo:

public class MyCommand
{
    public CustomerId { get; set; }

    // some other fields
}

public class MyCommandHandler<MyCommand> : ICommandHandler<MyCommand, CommandResult>
{
    Handle(MyCommand command)
    {
        // some code for saving Customer entity

        return CommandResult.Success;
    }
}

Mi chiedo perché non ci sia solo la classe Command che contiene il metodo di gestione dei dati e ? È un vantaggio di testabilità, in cui è necessario testare la logica di gestione dei comandi separatamente dalle proprietà del comando? O è un requisito aziendale frequente, in cui è necessario disporre di un comando gestito da diverse implementazioni di ICommandHandler<MyCommand, CommandResult> ?

    
posta rgripper 10.01.2014 - 20:24
fonte

2 risposte

13

Divertente, questa domanda mi ha appena ricordato esattamente la stessa conversazione che ho avuto con uno dei nostri ingegneri sulla libreria di comunicazioni su cui stavo lavorando.

Al posto dei comandi, avevo le classi Request e poi avevo RequestHandlers. Il design era molto simile a quello che stai descrivendo. Penso che parte della confusione che hai è che vedi la parola inglese "command", e immediatamente pensi "verb, action ... etc".

Ma in questo disegno, pensa a Comando (o Richiesta) come una lettera. O per quelli che non sanno che cos'è un servizio postale, pensa alla posta elettronica. È semplicemente contenuto, disaccoppiato dal modo in cui il contenuto dovrebbe essere interpretato.

Perché dovresti farlo? Nella maggior parte dei casi semplici, di Command Pattern non vi è alcun motivo e si potrebbe avere questa classe eseguire il lavoro direttamente. Tuttavia, fare il disaccoppiamento come nel tuo progetto ha senso se la tua azione / comando / richiesta deve percorrere una certa distanza. Ad esempio, across, socket o pipe o tra dominio e infrastruttura. O forse nella tua architettura i tuoi comandi devono essere persistenti (ad esempio, il gestore di comandi può eseguire 1 comando alla volta, a causa di alcuni eventi di sistema, arrivano 200 comandi e dopo che i primi 40 processi vengono arrestati). In tal caso, avendo una semplice classe di solo messaggio diventa molto semplice serializzare solo la parte del messaggio in JSON / XML / binary / qualunque e passarla lungo la pipeline fino a quando il suo gestore di comandi è pronto per elaborarla.

Un altro vantaggio del comando disaccoppiamento di CommandHandler è che ora hai l'opzione della gerarchia di ereditarietà parallela. Ad esempio, tutti i comandi potrebbero derivare da una classe di comando di base che supporta la serializzazione. E forse hai 4 gestori di comandi su 20 che hanno molte somiglianze, ora puoi ricavarne quelli della classe base di handler venuti. Se dovessi avere dati e gestione dei comandi in una classe, questo tipo di relazione verrebbe rapidamente fuori controllo.

Un altro esempio per il disaccoppiamento sarebbe se il comando richiedesse un input molto piccolo (ad esempio 2 interi e una stringa), ma la sua logica di gestione era abbastanza complessa in cui si desidera memorizzare i dati nelle variabili membro intermedie. Se si accodano 50 comandi, non si desidera allocare memoria per tutto lo storage intermedio, quindi separare Command da CommandHandler. Ora accodate 50 strutture di dati leggeri e una più complessa archiviazione dei dati viene assegnata una sola volta (o N volte se avete N gestori) da CommandHandler che sta elaborando i comandi.

    
risposta data 10.01.2014 - 23:33
fonte
2

Il normale schema di comando riguarda il fatto di avere i dati e il comportamento in una singola classe. Questo tipo di "schema Comando / Gestore" è leggermente diverso. L'unico vantaggio rispetto al modello normale è il vantaggio aggiunto di non avere il tuo comando dipendente dai framework. Ad esempio, il tuo comando potrebbe richiedere l'accesso al DB, quindi deve avere una sorta di contesto o sessione DB, il che significa che dipende dai framework. Ma questo comando potrebbe far parte del tuo dominio, quindi non vuoi che dipenda dai framework in base al Principio di inversione delle dipendenze . Separare i parametri di input e output dal comportamento e disporre di un dispatcher per collegarli può risolvere il problema.

D'altro canto, perderai il vantaggio sia dell'ereditarietà sia della composizione dei comandi. Quale penso sia il vero potere.

Inoltre, nitpick minore. Solo perché ha il comando in nome non fa parte di CQRS. Si tratta di qualcosa di molto più fondamentale. Questo tipo di struttura può servire sia come comando che come query anche nello stesso momento.

    
risposta data 10.01.2014 - 23:11
fonte

Leggi altre domande sui tag