2012-06-08 7 views
5

Hola estoy usando la biblioteca Simple Injector DI y han estado siguiendo algún material realmente interesante de un modelo arquitectónico diseñado en torno al patrón de comando:Calling comandos desde dentro de otro método de la manija de mando()

El contenedor administrará la vida útil del UnitOfWork, y estoy usando comandos para realizar funciones específicas en la base de datos.

Mi pregunta es si tengo un comando, por ejemplo un AddNewCustomerCommand, que a su vez realiza otra llamada a otro servicio (es decir, envía un mensaje de texto), desde un punto de vista de diseño es aceptable o debe hacerse a una mayor nivel y, de ser así, ¿cuál es la mejor manera de hacerlo?

Ejemplo de código es el siguiente:

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); 
    } 
} 

Respuesta

14

Depende de su punto de vista arquitectónico de comandos (de negocios), pero es muy natural para tener una relación uno a uno entre un Use Case y un comando. En ese caso, la capa de presentación debe (durante una sola acción del usuario, como un clic de botón) hacer nada más que crear el comando y ejecutarlo. Además, no debería hacer nada más que ejecutar ese único comando, nunca más. Todo lo que se necesita para realizar ese caso de uso, debe hacerse con ese comando.

Dicho esto, enviar mensajes de texto, escribir en la base de datos, realizar cálculos complejos, comunicarse con servicios web y todo lo demás que necesite para manejar las necesidades del negocio debe hacerse durante el contexto de ese comando (o tal vez en cola suceder más tarde). No antes, no después, ya que es ese comando el que representa los requisitos, en una presentación agnóstica.

Esto no significa que el controlador de comandos en sí mismo deba hacer todo esto. Será muy natural trasladar mucha lógica a otros servicios de los que depende el controlador. Así que puedo imaginar su manejador dependiendo de una interfaz ITextMessageSender, por ejemplo.

Otra discusión es si los manejadores de comandos deberían depender de otros manejadores de comandos dependientes. Cuando se analizan los casos de uso, no es poco probable que los casos de uso grandes consistan en múltiples casos de subuso más pequeños, por lo que en ese sentido no es extraño. De nuevo, habrá un mapeo uno a uno entre comandos y casos de uso.

Sin embargo, tenga en cuenta que tener un gráfico de dependencia profundo de los controladores de comando anidados uno depende del otro, puede complicar la navegación a través del código, así que échele un buen vistazo a esto. Podría ser mejor inyectar un ITextSessageSender en lugar de usar un ICommandHandler<SendTextMessageCommand>, por ejemplo.

Otro inconveniente de permitir que los manipuladores aniden, es que hace que las tareas de infraestructura sean un poco más complejas. Por ejemplo, al envolver controladores de comando con un decorador que agrega comportamiento transaccional, debe asegurarse de que los controladores anidados se ejecuten en la misma transacción que el controlador externo. Hoy he ayudado a un cliente mío con esto. No es increíblemente difícil, pero toma un poco de tiempo descubrirlo. Lo mismo vale para cosas como la detección de punto muerto, ya que esto también se ejecuta en el límite de la transacción.

Además, la detección de interbloqueo es un gran ejemplo para mostrar el poder de este patrón de comando/controlador, ya que casi cualquier otro estilo arquitectónico imposibilitará el complemento de este comportamiento. Eche un vistazo a la clase DeadlockRetryCommandHandlerDecorator en this article) para ver un ejemplo.

+0

Entonces, ¿cómo evitar que un comando enviado por un controlador de comando ejecute esos decoradores de punto muerto o de transacción? – GFoley83

+0

Deberá evitar que los decoradores se apliquen a los manejadores anidados (lo cual es realmente difícil de lograr con cualquier contenedor DI), o dejar que el decorador detecte que ya se ejecuta en el contexto de una transacción. Otra opción es dar a los manejadores anidados su propia abstracción. Esto hace que sea trivial aplicar decoradores solo al controlador externo. – Steven

Cuestiones relacionadas