design patterns - pattern - ¿Cuál es la diferencia entre Command+CommandHandler y Service?
state pattern (2)
He estado leyendo sobre el uso de objetos de Comando para representar los casos de uso que nuestro dominio expone, y los objetos del Controlador de Comandos para procesar esos comandos.
Por ejemplo:
RegisterUserCommand
-
RegisterUserCommandHandler
Pero parece exactamente lo mismo que tener un RegisterUserService
, donde el objeto de comando representaría los parámetros al método registerUser()
.
Y, por supuesto, si el método tuviera demasiados parámetros, terminaría creando un objeto para envolverlos y ese objeto sería el mismo que el RegisterUserCommand
.
Entonces, ¿por qué tener un patrón diferente para representar lo mismo? Los servicios están muy extendidos, no los Comandos (desde mi experiencia); ¿Cuál es la diferencia aquí que me estoy perdiendo? En resumen, ¿por qué usaría uno en lugar de otro?
Creo que tiene toda la razón al cuestionar que estos dos conceptos parecen ser similares en su contexto. Probablemente valga la pena volver atrás y considerar, prácticamente, a qué están destinados.
Servicios DDD
En Diseño impulsado por dominios, hay diferentes tipos de servicios, por ejemplo, Servicios de aplicación (generalmente servicios de IU), Servicios de infraestructura y Servicios de dominio.
Jimmy Bogard hace un excelente trabajo explicando estos
En una palabra:
Servicios de dominio
El trabajo de los servicios de dominio es ejecutar una funcionalidad que normalmente no se adapta a una entidad. Considere usar un servicio de dominio cuando tenga una funcionalidad que requiera una variedad de
Entidades (objetos agregados / valor). Un ejemplo tal vez: para calcular una estimación de cuánto puede costar una hipoteca, necesita los detalles sobre el ingreso / empleo del comprador. Puede requerir el historial crediticio del comprador y, finalmente, puede necesitar información sobre el edificio para el cual se está considerando la hipoteca.
pricingService.CalculateMortageEstimate(BuyerIncomingDetails bid, BuyerCreditHistory bch, BuildingOverview bo)
Servicios de Aplicación
Un ejemplo puede ser servicios utilizados como parte de la interfaz de usuario.
Servicios de infraestructura
Servicios que tienden a comunicarse con recursos externos (remitentes de correo electrónico, sistemas de archivos, archivos xml, ftp, etc.)
Command / CommandHandlers (CQRS)
Comando de consulta de la responsabilidad de la segregación. Como se indica en el envase; una separación de:
- ejecutando consultas contra su fuente de datos
- Modificando (a través de comandos) sus datos
El uso de CQRS no siempre es la opción correcta, pero en mi experiencia, las personas tienden a usarla cuando sus datos se distribuyen en múltiples fuentes de datos.
Así que con los comandos, está pidiendo explícitamente que se ejecute una unidad de trabajo (que no debe confundirse con el patrón UnitOfWork), por ejemplo, AddFraudRecordCommand o UpdateNoteCommand.
Con ese pequeño refrigerio sobre las diferencias entre los servicios DDD y los comandos CQRS. Me gustaría señalar las siguientes cosas:
¿Necesito incluso Command / CommandHandlers? ¿Qué estoy ganando, debo ir directamente a los servicios?
El trabajo de mi controlador de comandos es manejar la lógica de mi comando (un comando es una solicitud muy específica). Mientras que los servicios DDD tienen diferentes trabajos (Servicios de dominio: coordina la funcionalidad de varias entidades, Servicios de infraestructura: colabora con servicios externos, por ejemplo, correo electrónico)
Tal vez piense así: Trabajo de CommandHandler: ejecute el código para ejecutar el comando específico (esto puede incluir el uso de múltiples servicios). Trabajo de servicio - Dependiendo de qué tipo de servicio es.
No es el mejor ejemplo, pero espero que brille un poco de luz sobre lo que trato de decir:
public class CalculateFraudProbabilityCommandHandler : CommandHandler<CalculateFraudProbabilityCommand>
{
IFraudService _fraudService;
IEmailNotifier _notifier;
ICustomerRepository _customerRepo;
public CalculateFraudProbabilityCommandHandler(ICustomerRepository customerRepo, IFraudService fraudService, IEmailNotifier notifier)
{
_fraudService = fraudService; //Domain Service
_notifier = notifier; //Infrastructure Service
_customerRepo = customerRepo; //Repository
}
//Execute Command
public void Execute(CalculateFraudProbabilityCommand command) {
Customer customer = _customerRepository.GetById(command.CustomerId);
FraudHistory fraudHistory = _fraudService.RetrieveFraudHistory(customer);
//any fraud recently? if so, let someone know!
if(fraudHistory.FraudSince(DateTime.Now.AddYear(-1)) {
_notifier.SendEmail(_fraudService.BuildFraudWarningEmail(customer, fraudHistory));
}
}
}
Tener Comandos le da los beneficios del buen viejo patrón de Comando:
- puede parametrizar un objeto, por ejemplo, un elemento UI, con un comando para ejecutar
- puede almacenar un comando y ejecutarlo más tarde, por ejemplo, en una cola o en un registro de transacciones
- puede realizar un seguimiento de los comandos que ejecutó, lo que le brinda una base para implementar el deshacer
Si sus servicios eran grandes, cada uno con muchos métodos complejos (y si los métodos no eran complejos, probablemente no debería usar DDD o CQRS), entonces mover cada método a un Controlador de Comandos podría mejorar su aplicación al hacerla más compostable. más fácil de probar, etc. Sin duda, es común que las personas que refactorizan directamente desde grandes servicios a Comandos / Comandos de Comandos consideren esto como un beneficio de este último patrón. Pero puede obtener el mismo beneficio al descomponer los servicios grandes en otros más pequeños (como lo sugiere el servicio muy específico en su ejemplo), por lo que, estrictamente hablando, no hay una diferencia entre los servicios y los Comandos / Controladores de Comandos.