2012-06-08 3 views
5

Ciao Sto usando la biblioteca Simple Injector DI e hanno seguito un po 'di materiale veramente interessante di un modello architettonico progettato intorno al modello di comando:Calling comandi dal all'interno di un altro maniglia di comando() metodo

Il contenitore gestirà la durata del UnitOfWork e sto utilizzando i comandi per eseguire funzioni specifiche nel database.

La mia domanda è se ho un comando, ad esempio un AddNewCustomerCommand, che a sua volta esegue un'altra chiamata ad un altro servizio (cioè invia un messaggio di testo), dal punto di vista di design è questo accettabile o questo dovrebbe essere fatto in un più alto livello e se sì, come meglio farlo?

codice di esempio è qui sotto:

public class AddNewBusinessUnitHandler 
    : ICommandHandler<AddBusinessUnitCommand> 
{ 
    private IUnitOfWork uow; 
    private ICommandHandler<OtherServiceCommand> otherHandler; 

    AddNewBusinessUnitHandler(IUnitOfWork uow, 
     ICommandHandler<OtherServiceCommand> otherHandler) 
    { 
     this.uow = uow; 
     this.otherHandler = otherHandler; 
    } 

    public void Handle(AddBusinessUnitCommand command) 
    { 
     var businessUnit = new BusinessUnit() 
     { 
      Name = command.BusinessUnitName, 
      Address = command.BusinessUnitAddress 
     }; 

     var otherCommand = new OtherServiceCommand() 
     { 
      welcomePostTo = command.BusinessUnitName 
     }; 

     uow.BusinessUnitRepository.Add(businessUnit); 

     this.otherHandler.Handle(otherCommand); 
    } 
} 

risposta

14

Essa dipende dalla vostra vista architettonico dei comandi (business), ma è del tutto naturale ad avere una mappatura uno a uno tra un Use Case e un comando. In tal caso, il livello di presentazione dovrebbe (durante un'azione di un singolo utente, come un clic del pulsante) non fare altro che creare il comando ed eseguirlo. Inoltre, non dovrebbe fare altro che eseguire il comando singolo, mai più. Tutto ciò che è necessario per eseguire tale caso d'uso, dovrebbe essere fatto da quel comando.

Detto questo, l'invio di messaggi di testo, la scrittura nel database, l'esecuzione di calcoli complessi, la comunicazione con i servizi Web e tutto ciò che è necessario per gestire le esigenze dell'azienda devono essere eseguiti nel contesto di tale comando (o forse in coda a capita più tardi). Non prima, non dopo, dal momento che è quel comando che rappresenta i requisiti, in una presentazione agnostica.

Ciò non significa che lo stesso gestore comandi debba eseguire tutto ciò. Sarà del tutto naturale trasferire molta logica ad altri servizi da cui dipende il gestore. Quindi posso immaginare il tuo gestore in base a un'interfaccia ITextMessageSender, per esempio.

Un'altra discussione è se i gestori di comandi devono dipendere da altri gestori di comandi dipendenti. Quando si considerano i casi d'uso, non è improbabile che i grandi casi d'uso siano costituiti da più casi di utilizzo secondario più piccoli, quindi in questo senso non è strano. Di nuovo, ci sarà una mappatura uno a uno tra comandi e casi d'uso.

Tuttavia, si noti che avere un grafico di dipendenza profondo di gestori di comandi annidati a seconda l'uno dell'altro può complicare la navigazione nel codice, quindi date un'occhiata a questo. Potrebbe essere meglio iniettare un ITextSessageSender invece di usare un ICommandHandler<SendTextMessageCommand>, per esempio.

Un altro svantaggio di consentire ai gestori di nidificare, è che rende le cose infrastrutturali un po 'più complesse. Ad esempio, quando si avvolgono i gestori di comandi con un decoratore che aggiunge un comportamento transazionale, è necessario assicurarsi che i gestori nidificati vengano eseguiti nella stessa transazione del gestore più esterno. Mi è capitato di aiutare un cliente con me oggi. Non è incredibilmente difficile, ma richiede un po 'di tempo per capirlo. Lo stesso vale per cose come il rilevamento di deadlock, poiché questo funziona anche al confine della transazione.

Inoltre, il rilevamento di deadlock è un ottimo esempio per mostrare il potere di questo modello di comando/gestore, poiché quasi ogni altro stile architettonico renderà impossibile il plug-in di questo comportamento. Dai un'occhiata alla classe DeadlockRetryCommandHandlerDecorator in this article) per vedere un esempio.

+0

Quindi, come si impedisce a un comando inviato da un gestore di comandi di eseguire quei deadlock o decoratori di transazioni? – GFoley83

+0

È necessario impedire che i decoratori vengano applicati ai gestori nidificati (che in genere è davvero difficile da ottenere con qualsiasi contenitore DI) oppure lasciare che lo stesso decoratore rilevi che è già in esecuzione nel contesto di una transazione. Un'altra opzione è di dare ai gestori annidati la propria astrazione. Ciò rende banale applicare solo decoratori al gestore esterno. – Steven

Problemi correlati