Skip to content
StackPractices
intermediate Por Mathias Paulenko

Patrón Command

Encapsula una petición como un objeto, permitiendo parametrizar clientes con colas, logs y operaciones deshacibles. Patrón de diseño conductual.

Temas: design

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Patrón Command

Visión general

El Patrón Command es un patrón de diseño conductual que convierte una petición en un objeto independiente que contiene toda la información sobre la petición. Esto te permite parametrizar métodos con diferentes peticiones, retrasar o encolar ejecución y soportar operaciones deshacibles.

Es la base de sistemas de undo/redo, colas de trabajo, grabación de macros y operaciones transaccionales.

Cuándo usarlo

Usa el Patrón Command cuando:

  • Necesitas parametrizar objetos con operaciones a ejecutar
  • Quieres encolar, programar o ejecutar operaciones remotamente
  • Necesitas funcionalidad de deshacer/rehacer
  • Quieres registrar cambios para reproducción o auditoría
  • Necesitas comportamiento transaccional (ejecutar todo o revertir)

Solución

Python

from abc import ABC, abstractmethod

class Command(ABC):
    @abstractmethod
    def execute(self):
        pass

    @abstractmethod
    def undo(self):
        pass

class Light:
    def __init__(self):
        self.is_on = False

    def turn_on(self):
        self.is_on = True
        print("Light is on")

    def turn_off(self):
        self.is_on = False
        print("Light is off")

class TurnOnCommand(Command):
    def __init__(self, light: Light):
        self.light = light

    def execute(self):
        self.light.turn_on()

    def undo(self):
        self.light.turn_off()

# Uso
light = Light()
cmd = TurnOnCommand(light)
cmd.execute()  # Light is on
cmd.undo()     # Light is off

JavaScript

class Light {
  constructor() {
    this.isOn = false;
  }
  turnOn() {
    this.isOn = true;
    console.log("Light is on");
  }
  turnOff() {
    this.isOn = false;
    console.log("Light is off");
  }
}

class TurnOnCommand {
  constructor(light) {
    this.light = light;
  }
  execute() {
    this.light.turnOn();
  }
  undo() {
    this.light.turnOff();
  }
}

// Uso
const light = new Light();
const cmd = new TurnOnCommand(light);
cmd.execute(); // Light is on
cmd.undo();    // Light is off

Java

interface Command {
    void execute();
    void undo();
}

class Light {
    boolean isOn = false;
    void turnOn() { isOn = true; System.out.println("Light is on"); }
    void turnOff() { isOn = false; System.out.println("Light is off"); }
}

class TurnOnCommand implements Command {
    private final Light light;
    TurnOnCommand(Light light) { this.light = light; }
    public void execute() { light.turnOn(); }
    public void undo() { light.turnOff(); }
}

// Uso
Light light = new Light();
Command cmd = new TurnOnCommand(light);
cmd.execute(); // Light is on
cmd.undo();    // Light is off

Explicación

El Patrón Command separa la invocación de la acción de su ejecución:

  • Interfaz Command: Declara execute() y opcionalmente undo()
  • Command concreto (TurnOnCommand): Vincula un receptor (Light) a una acción (turnOn)
  • Receptor (Light): El objeto que realiza el trabajo real
  • Invocador: Llama execute() en los commands (ej. un botón, scheduler o control remoto)

Al encapsular peticiones como objetos, ganas la habilidad de encolar, loggear y revertir operaciones.

Variantes

VarianteCaso de usoCompromiso
Command simpleAcción directa sin undoFácil de implementar, flexibilidad limitada
Command deshacibleOperaciones que pueden revertirseRequiere mantener estado para la reversión
Macro CommandCompuesto de múltiples commandsPotente, pero más difícil de deshacer atómicamente

Lo que funciona

  • Implementa undo() para cada command si tu sistema soporta deshacer
  • Mantén los commands sin estado cuando sea posible: Almacena estado del receptor, no del command
  • Usa un historial de commands (stack) para soportar deshacer/rehacer multinivel
  • Documenta efectos secundarios: Commands que afectan sistemas externos son más difíciles de deshacer
  • Considera inmutabilidad: Una vez configurado, un command no debería cambiar su target

Errores comunes

  • Olvidar estado de undo: Commands que no pueden revertirse rompen el stack de undo
  • Acoplamiento fuerte: Commands que dependen de estado global en lugar de un receptor específico
  • Sobre-ingeniería: Usar Command para operaciones triviales que nunca necesitan encolado o deshacer
  • Suposiciones síncronas: No considerar que los commands pueden ejecutarse asíncronamente
  • Falta de idempotencia: Ejecutar el mismo command dos veces produce resultados diferentes
  • No manejar fallos de command: Commands que lanzan excepciones pueden dejar el sistema en un estado inconsistente
  • Almacenar demasiado estado en commands: Los commands deberían ser ligeros; almacenar objetos grandes afecta memoria y serialización
  • Ignorar seguridad de threads: Commands ejecutados concurrentemente pueden acceder recursos compartidos sin sincronización apropiada
  • No loggear ejecución de command: Faltar trails de auditoría hace debugging y cumplimiento difícil
  • Mezclar preocupaciones: Commands que realizan múltiples responsabilidades no relacionadas violan el principio de responsabilidad única

Técnicas Avanzadas

Command con Parámetros

Los commands pueden aceptar parámetros para ejecución más flexible:

class SetValueCommand(Command):
    def __init__(self, receiver, key, value):
        self.receiver = receiver
        self.key = key
        self.value = value
        self.previous_value = None

    def execute(self):
        self.previous_value = self.receiver.get(self.key)
        self.receiver.set(self.key, self.value)

    def undo(self):
        self.receiver.set(self.key, self.previous_value)

Command Compuesto (Macro)

Combina múltiples commands en una sola unidad ejecutable:

class MacroCommand(Command):
    def __init__(self):
        self.commands = []

    def add(self, command: Command):
        self.commands.append(command)

    def execute(self):
        for cmd in self.commands:
            cmd.execute()

    def undo(self):
        for cmd in reversed(self.commands):
            cmd.undo()

Cola de Commands

Implementa una cola para ejecución diferida de commands:

from queue import Queue
import threading

class CommandQueue:
    def __init__(self):
        self.queue = Queue()
        self.running = False

    def enqueue(self, command: Command):
        self.queue.put(command)

    def start(self):
        self.running = True
        thread = threading.Thread(target=self._process)
        thread.daemon = True
        thread.start()

    def _process(self):
        while self.running:
            cmd = self.queue.get()
            try:
                cmd.execute()
            except Exception as e:
                print(f"Command falló: {e}")

Ejecución Async de Command

Soporta ejecución asíncrona de commands para operaciones I/O-bound:

import asyncio

class AsyncCommand(ABC):
    @abstractmethod
    async def execute(self):
        pass

    @abstractmethod
    async def undo(self):
        pass

class AsyncDatabaseCommand(AsyncCommand):
    def __init__(self, db, query):
        self.db = db
        self.query = query

    async def execute(self):
        await self.db.execute(self.query)

    async def undo(self):
        await self.db.rollback()

Command con Timeout

Añade capacidad de timeout para prevenir commands colgados:

import signal
from contextlib import contextmanager

class TimeoutCommand(Command):
    def __init__(self, command: Command, timeout: int):
        self.command = command
        self.timeout = timeout

    @contextmanager
    def time_limit(self, seconds):
        def signal_handler(signum, frame):
            raise TimeoutError("Timed out")
        signal.signal(signal.SIGALRM, signal_handler)
        signal.alarm(seconds)
        try:
            yield
        finally:
            signal.alarm(0)

    def execute(self):
        with self.time_limit(self.timeout):
            self.command.execute()

    def undo(self):
        with self.time_limit(self.timeout):
            self.command.undo()

Command con Lógica de Reintento

Implementa lógica de reintento para fallos transientes:

class RetryCommand(Command):
    def __init__(self, command: Command, max_retries=3, delay=1):
        self.command = command
        self.max_retries = max_retries
        self.delay = delay

    def execute(self):
        import time
        for attempt in range(self.max_retries):
            try:
                self.command.execute()
                return
            except Exception as e:
                if attempt == self.max_retries - 1:
                    raise
                time.sleep(self.delay * (attempt + 1))

    def undo(self):
        self.command.undo()

Command con Validación

Valida parámetros de command antes de ejecución:

class ValidatedCommand(Command):
    def __init__(self, command: Command, validator):
        self.command = command
        self.validator = validator

    def execute(self):
        if not self.validator.validate():
            raise ValueError("Validación de command falló")
        self.command.execute()

    def undo(self):
        self.command.undo()

Logging de Command

Logea todas las ejecuciones de command para trails de auditoría:

class LoggedCommand(Command):
    def __init__(self, command: Command, logger):
        self.command = command
        self.logger = logger

    def execute(self):
        self.logger.log(f"Ejecutando {self.command.__class__.__name__}")
        self.command.execute()
        self.logger.log(f"Completado {self.command.__class__.__name__}")

    def undo(self):
        self.logger.log(f"Deshaciendo {self.command.__class__.__name__}")
        self.command.undo()
        self.logger.log(f"Deshacer completado {self.command.__class__.__name__}")

Command con Soporte de Transacción

Implementa comportamiento transaccional para commands:

class TransactionalCommand(Command):
    def __init__(self, commands: list[Command]):
        self.commands = commands
        self.executed = []

    def execute(self):
        try:
            for cmd in self.commands:
                cmd.execute()
                self.executed.append(cmd)
        except Exception as e:
            self._rollback()
            raise

    def _rollback(self):
        for cmd in reversed(self.executed):
            try:
                cmd.undo()
            except Exception:
                pass
        self.executed.clear()

    def undo(self):
        for cmd in reversed(self.executed):
            cmd.undo()

Command con Cancelación

Soporta cancelación de command durante ejecución:

class CancellableCommand(Command):
    def __init__(self, command: Command):
        self.command = command
        self.cancelled = False

    def cancel(self):
        self.cancelled = True

    def execute(self):
        if self.cancelled:
            raise RuntimeError("Command fue cancelado")
        self.command.execute()

    def undo(self):
        self.command.undo()

Command con Tracking de Progreso

Rastrea progreso de commands de larga duración:

class ProgressCommand(Command):
    def __init__(self, command: Command, progress_callback):
        self.command = command
        self.progress_callback = progress_callback

    def execute(self):
        self.progress_callback(0, "Iniciando")
        self.command.execute()
        self.progress_callback(100, "Completado")

    def undo(self):
        self.progress_callback(0, "Deshaciendo")
        self.command.undo()
        self.progress_callback(100, "Deshacer completado")

Command con Gestión de Recursos

Asegura cleanup apropiado de recursos:

class ResourceCommand(Command):
    def __init__(self, command: Command, resource):
        self.command = command
        self.resource = resource

    def execute(self):
        self.resource.acquire()
        try:
            self.command.execute()
        finally:
            self.resource.release()

    def undo(self):
        self.resource.acquire()
        try:
            self.command.undo()
        finally:
            self.resource.release()

Command con Rate Limiting

Implementa rate limiting para ejecución de command:

import time

class RateLimitedCommand(Command):
    def __init__(self, command: Command, rate_limit, window):
        self.command = command
        self.rate_limit = rate_limit
        self.window = window
        self.executions = []

    def execute(self):
        now = time.time()
        self.executions = [t for t in self.executions if now - t < self.window]
        if len(self.executions) >= self.rate_limit:
            raise RuntimeError("Rate limit excedido")
        self.executions.append(now)
        self.command.execute()

    def undo(self):
        self.command.undo()

Command con Circuit Breaking

Añade circuit breaking para llamadas a servicios externos:

class CircuitBreakerCommand(Command):
    def __init__(self, command: Command, threshold=5, timeout=60):
        self.command = command
        self.threshold = threshold
        self.timeout = timeout
        self.failures = 0
        self.last_failure = None
        self.state = "closed"

    def execute(self):
        if self.state == "open":
            if time.time() - self.last_failure > self.timeout:
                self.state = "half-open"
            else:
                raise RuntimeError("Circuit breaker está abierto")

        try:
            self.command.execute()
            if self.state == "half-open":
                self.state = "closed"
                self.failures = 0
        except Exception as e:
            self.failures += 1
            self.last_failure = time.time()
            if self.failures >= self.threshold:
                self.state = "open"
            raise

    def undo(self):
        self.command.undo()

Command con Procesamiento por Lotes

Procesa múltiples commands en un lote:

class BatchCommand(Command):
    def __init__(self, commands: list[Command], batch_size=10):
        self.commands = commands
        self.batch_size = batch_size
        self.executed = []

    def execute(self):
        for i in range(0, len(self.commands), self.batch_size):
            batch = self.commands[i:i + self.batch_size]
            for cmd in batch:
                cmd.execute()
                self.executed.append(cmd)

    def undo(self):
        for cmd in reversed(self.executed):
            cmd.undo()

Command con Prioridad

Ejecuta commands basado en prioridad:

import heapq

class PriorityQueue:
    def __init__(self):
        self.queue = []
        self.counter = 0

    def enqueue(self, command: Command, priority: int):
        heapq.heappush(self.queue, (priority, self.counter, command))
        self.counter += 1

    def dequeue(self) -> Command:
        if self.queue:
            return heapq.heappop(self.queue)[2]
        raise IndexError("Cola está vacía")

Command con Gestión de Dependencias

Ejecuta commands respetando dependencias:

class DependencyCommand(Command):
    def __init__(self, command: Command, dependencies: list[Command]):
        self.command = command
        self.dependencies = dependencies

    def execute(self):
        for dep in self.dependencies:
            dep.execute()
        self.command.execute()

    def undo(self):
        self.command.undo()
        for dep in reversed(self.dependencies):
            dep.undo()

Command con Versioning

Soporta ejecución de command versionada:

class VersionedCommand(Command):
    def __init__(self, command: Command, version: str):
        self.command = command
        self.version = version

    def execute(self):
        print(f"Ejecutando versión {self.version}")
        self.command.execute()

    def undo(self):
        print(f"Deshaciendo versión {self.version}")
        self.command.undo()

Command con Colección de Métricas

Colecciona métricas para ejecución de command:

class MetricsCommand(Command):
    def __init__(self, command: Command, metrics_collector):
        self.command = command
        self.metrics = metrics_collector

    def execute(self):
        start_time = time.time()
        try:
            self.command.execute()
            self.metrics.record_success(time.time() - start_time)
        except Exception as e:
            self.metrics.record_failure(time.time() - start_time)
            raise

    def undo(self):
        start_time = time.time()
        self.command.undo()
        self.metrics.record_undo(time.time() - start_time)

Mejores Prácticas

  1. Mantén commands ligeros. Los commands deberían ser objetos pequeños y enfocados que encapsulan una sola acción. Evita almacenar grandes cantidades de datos en commands.

  2. Implementa undo para todos los commands que cambian estado. Si tu sistema soporta deshacer, cada command que cambie estado debería implementar lógica de undo.

  3. Usa factories de command para creación compleja. Cuando los commands requieren configuración compleja, usa métodos de factory o builders para encapsular lógica de creación.

  4. Maneja excepciones gracefulmente. Los commands deberían manejar sus propias excepciones o proporcionar información de error clara al invocador.

  5. Haz commands serializables. Si necesitas persistir commands o enviarlos por la red, asegúrate que puedan serializarse y deserializarse.

  6. Documenta efectos secundarios de command. Documenta claramente cualquier efecto secundario externo que un command pueda tener, especialmente aquellos difíciles de deshacer.

  7. Usa historial de command para debugging. Mantén un historial de commands ejecutados para ayudar con debugging y trails de auditoría.

  8. Considera composición de command. Usa commands compuestos para construir operaciones complejas de commands más simples y reutilizables.

  9. Valida parámetros de command. Valida parámetros de command antes de ejecución para fallar rápido y proporcionar mensajes de error claros.

  10. Prueba commands en aislamiento. Escribe pruebas unitarias para cada command independientemente, luego pruebas de integración para cadenas de command y macros.

Preguntas Frecuentes

P: ¿Cuál es la diferencia entre Command y Strategy? R: Strategy encapsula algoritmos intercambiables. Command encapsula una petición para realizar una acción, a menudo con soporte para deshacer, encolar y logging.

P: ¿Se puede usar Command sin undo? R: Sí. La capacidad de deshacer es opcional. Muchos sistemas usan Command únicamente para encolar y desacoplar invocadores de receptores.

P: ¿Cómo implemento deshacer multinivel? R: Mantén un stack de commands ejecutados. Deshacer hace pop del stack y llama undo(). Consulta Command con Undo/Redo para una implementación completa. Rehacer empuja el command de vuelta y llama execute().

P: ¿Pueden los commands ejecutarse asíncronamente? R: Sí. Implementa interfaces de command async con métodos execute() y undo() que retornan promesas o usan patrones async/await. Esto es útil para operaciones I/O-bound como llamadas a base de datos o peticiones de red.

P: ¿Cómo manejo fallos de command? R: Implementa manejo de errores a nivel de command o usa decoradores de command que envuelven ejecución con bloques try-catch. Considera implementar lógica de reintento, circuit breaking o mecanismos de fallback para fallos transientes.

P: ¿Deberían los commands ser serializables? R: Sí, si necesitas persistir commands para replay, trails de auditoría o ejecución distribuida. Asegúrate que los commands puedan serializarse a JSON, binario u otro formato y deserializarse sin perder estado.

P: ¿Cómo implemento encolado de commands? R: Usa una estructura de datos de cola para mantener commands y un hilo o proceso worker para ejecutarlos. Esto habilita ejecución diferida, procesamiento en background y balanceo de carga across workers.

P: ¿Pueden los commands componerse? R: Sí. Usa commands compuestos (macro commands) para combinar múltiples commands en una sola unidad ejecutable. Esto es útil para operaciones complejas que requieren múltiples pasos ejecutados atómicamente.

P: ¿Cómo implemento logging de command? R: Envuelve commands con decoradores de logging que registran detalles de ejecución, parámetros y resultados. Esto proporciona trails de auditoría e información de debugging para ejecución de command.

P: ¿Deberían los commands validar sus parámetros? R: Sí. Valida parámetros de command antes de ejecución para fallar rápido y proporcionar mensajes de error claros. Esto previene que commands inválidos se ejecuten y causen estado inconsistente.

P: ¿Cómo implemento cancelación de command? R: Añade soporte de cancelación incluyendo un token de cancelación o flag en el command. Verifica el estado de cancelación durante ejecución y aborta si se solicita cancelación.

P: ¿Pueden los commands tener dependencias? R: Sí. Implementa gestión de dependencias donde los commands especifican sus dependencias y se ejecutan en el orden correcto. Esto asegura que commands de prerequisite completen antes que commands dependientes ejecuten.

P: ¿Cómo implemento versioning de command? R: Incluye información de versión en commands e implementa lógica de ejecución específica de versión. Esto permite compatibilidad backward y migración entre versiones de command.

P: ¿Deberían los commands ser thread-safe? R: Sí, si los commands se ejecutan concurrentemente. Asegúrate que los commands no compartan estado mutable o usa mecanismos de sincronización apropiados cuando acceden recursos compartidos.

P: ¿Cómo implemento colección de métricas de command? R: Envuelve commands con colectores de métricas que registran tiempo de ejecución, tasas de éxito/fallo y otras métricas de rendimiento. Usa estos datos para monitoreo y optimización.

P: ¿Pueden los commands ejecutarse con timeout? R: Sí. Implementa decoradores de timeout que abortan ejecución de command si excede un límite de tiempo especificado. Esto previene que commands colgados bloqueen el sistema.

P: ¿Cómo implemento rate limiting de command? R: Añade lógica de rate limiting a commands para controlar la frecuencia de ejecución. Esto es útil para prevenir abuso y gestionar uso de recursos.

P: ¿Deberían los commands soportar idempotencia? R: Sí, para commands que pueden reintentarse o ejecutarse múltiples veces. Asegúrate que ejecutar el mismo command múltiples veces produce el mismo resultado.

P: ¿Cómo implemento transacciones de command? R: Usa commands transaccionales que ejecutan múltiples commands atómicamente. Si algún command falla, revierte todos los commands ejecutados para mantener consistencia.

P: ¿Pueden los commands ejecutarse con prioridad? R: Sí. Implementa colas de prioridad donde commands con prioridad más alta se ejecutan antes que commands con prioridad más baja. Esto es útil para asegurar que operaciones críticas se procesen primero.

P: ¿Cómo implemento tracking de progreso de command? R: Añade callbacks o eventos de progreso a commands que reportan progreso de ejecución. Esto es útil para commands de larga duración para proporcionar feedback a usuarios.

P: ¿Deberían los commands gestionar sus propios recursos? R: Sí, si los commands adquieren recursos como conexiones de base de datos o handles de archivos. Usa patrones de gestión de recursos para asegurar cleanup apropiado incluso si la ejecución falla.

P: ¿Cómo implemento circuit breaking de command? R: Añade lógica de circuit breaking a commands que detectan fallos y detienen ejecución cuando se alcanza un umbral. Esto previene fallos en cascada en sistemas distribuidos.

P: ¿Pueden los commands procesarse en lotes? R: Sí. Implementa procesamiento por lotes donde múltiples commands se ejecutan juntos para eficiencia. Esto es útil para operaciones bulk y reducir overhead.

P: ¿Cómo implemento replay de command? R: Almacena commands ejecutados en un log de historial y repláyalos en el mismo orden. Esto es útil para reproducir estado, testing y recuperación de desastres.

P: ¿Deberían los commands ser inmutables? R: Idealmente sí. Una vez configurado, un command no debería cambiar su target o parámetros. Esto hace commands más seguros de usar en entornos concurrentes y distribuidos.

P: ¿Cómo implemento scheduling de command? R: Usa schedulers o colas de trabajo para ejecutar commands en tiempos específicos o intervalos. Esto habilita ejecución diferida y tareas periódicas.

P: ¿Pueden los commands ejecutarse remotamente? R: Sí. Serializa commands y envíalos a workers remotos para ejecución. Esto habilita procesamiento distribuido y balanceo de carga across múltiples máquinas.

P: ¿Cómo implemento validación de command? R: Usa validadores para verificar parámetros de command y precondiciones antes de ejecución. Esto asegura que los commands son válidos y pueden ejecutarse exitosamente.

P: ¿Deberían los commands soportar rollback? R: Sí, para commands que modifican estado. Implementa lógica de rollback que revierte cambios si la ejecución falla o se cancela.

P: ¿Cómo implemento composición de command con decoradores? R: Usa patrones de decorador para añadir preocupaciones cross-cutting como logging, validación, reintento y métricas a commands sin modificar la implementación de command.

P: ¿Pueden los commands ejecutarse condicionalmente? R: Sí. Añade lógica condicional a commands o usa filtros de command que determinan si un command debería ejecutarse basado en condiciones de runtime.

P: ¿Cómo implemento inyección de dependencias de command? R: Usa inyección de dependencias para proporcionar receptores y otras dependencias a commands. Esto hace commands más testeables y flexibles.

P: ¿Deberían los commands implementar recuperación de errores? R: Sí, para commands que pueden encontrar fallos transientes. Implementa lógica de reintento, mecanismos de fallback o paths de ejecución alternativos.

P: ¿Cómo implemento gestión de estado de command? R: Almacena estado de command en el command mismo o en un gestor de estado separado. Esto habilita tracking de ejecución de command y soportar undo/redo.

P: ¿Pueden los commands ejecutarse en paralelo? R: Sí, para commands independientes. Usa patrones de ejecución paralela para mejorar rendimiento para commands que pueden ejecutarse concurrentemente sin conflictos.

P: ¿Cómo implemento seguridad de command? R: Añade checks de autorización y autenticación a commands. Asegúrate que solo usuarios o servicios autorizados puedan ejecutar commands sensibles.

P: ¿Deberían los commands soportar múltiples receptores? R: Sí, para commands que necesitan actuar en múltiples objetos. Implementa patrones de broadcast o multicast para ejecutar commands en múltiples receptores.

P: ¿Cómo implemento gestión de lifecycle de command? R: Define etapas de lifecycle para commands (creado, encolado, ejecutando, completado, fallido) y gestiona transiciones entre etapas. Esto proporciona visibilidad en ejecución de command.

P: ¿Pueden los commands ejecutarse con compensación? R: Sí. Implementa acciones de compensación que revierten los efectos de un command si no puede deshacerse directamente. Esto es útil para operaciones complejas.

P: ¿Cómo implemento monitoreo de command? R: Añade hooks de monitoreo a commands que reportan estado de ejecución, métricas de rendimiento y errores. Usa estos datos para visibilidad operacional y alerting.

P: ¿Deberían los commands soportar configuración? R: Sí. Haz commands configurables a través de parámetros u objetos de configuración. Esto permite que el mismo command se use en diferentes contextos con diferentes configuraciones.

P: ¿Cómo implemento persistencia de command? R: Persiste commands a una base de datos o sistema de archivos para replay, trails de auditoría o recuperación. Asegúrate que serialización y deserialización preserven estado de command.

P: ¿Pueden los commands ejecutarse con contexto? R: Sí. Pasa información de contexto (usuario, ID de petición, ID de correlación) a commands para tracing y ejecución contextual. Esto mejora debugging y observabilidad.

P: ¿Cómo implemento validación de command en runtime? R: Usa validación en runtime para verificar precondiciones e invariantes de command durante ejecución. Esto captura errores temprano y proporciona mensajes de error claros.

P: ¿Deberían los commands soportar múltiples modos de ejecución? R: Sí. Implementa diferentes modos de ejecución (síncrono, asíncrono, fire-and-forget) para soportar diferentes casos de uso y requisitos de rendimiento.

P: ¿Cómo implemento reintento de command con backoff? R: Implementa lógica de reintento con backoff exponencial para fallos transientes. Esto mejora resiliencia para commands que pueden fallar debido a problemas temporales.

P: ¿Pueden los commands ejecutarse con deadlines? R: Sí. Añade lógica de deadline o timeout a commands que abortan ejecución si no se completan por un tiempo especificado. Esto previene agotamiento de recursos.

P: ¿Cómo implemento procesamiento por lotes de command con agregación? R: Procesa múltiples commands y agrega sus resultados para eficiencia. Esto es útil para operaciones bulk y reducir round trips.

P: ¿Deberían los commands soportar múltiples estrategias de undo? R: Sí. Implementa diferentes estrategias de undo (inmediato, diferido, compensación) basado en tipo de command y requisitos.

P: ¿Cómo implemento migración de versión de command? R: Proporciona lógica de migración para convertir commands entre versiones. Esto asegura compatibilidad backward cuando los schemas de command evolucionan.

P: ¿Pueden los commands ejecutarse con cuotas de recursos? R: Sí. Implementa gestión de cuotas de recursos para limitar los recursos (CPU, memoria, I/O) que los commands pueden consumir. Esto previene agotamiento de recursos.

P: ¿Cómo implemento boundaries de error de command? R: Usa boundaries de error para aislar fallos de command y prevenir errores en cascada. Esto mejora resiliencia del sistema y tolerancia a fallos.

P: ¿Deberían los commands soportar múltiples formatos de resultado? R: Sí. Permite que los commands retornen resultados en diferentes formatos (JSON, XML, binario) basado en requisitos del cliente. Esto mejora flexibilidad.

P: ¿Cómo implemento caché de command? R: Cachea resultados de command para commands idempotentes para evitar ejecución redundante. Esto mejora rendimiento para operaciones costosas.

P: ¿Pueden los commands ejecutarse con throttling? R: Sí. Implementa throttling para controlar la tasa de ejecución de command. Esto previene overload y asegura asignación justa de recursos.

P: ¿Cómo implemento sincronización de estado de command? R: Sincroniza estado de command across sistemas distribuidos usando protocolos de consenso o stores de estado distribuidos. Esto asegura consistencia en entornos distribuidos.

P: ¿Deberían los commands soportar múltiples contextos de ejecución? R: Sí. Permite que los commands ejecuten en diferentes contextos (usuario, sistema, background) con diferentes permisos y límites de recursos.

P: ¿Cómo implemento orquestación de command? R: Usa orquestadores para coordinar la ejecución de múltiples commands con dependencias, condiciones y manejo de errores. Esto habilita workflows complejos.

P: ¿Pueden los commands ejecutarse con claves de idempotencia? R: Sí. Usa claves de idempotencia para asegurar que commands duplicados no se ejecuten múltiples veces. Esto es importante para sistemas distribuidos.

P: ¿Cómo implemento event sourcing de command? R: Almacena ejecución de command como eventos en un log de eventos. Esto proporciona un trail de auditoría completo y habilita replay de eventos para reconstrucción de estado.

P: ¿Deberían los commands soportar múltiples tipos de resultado? R: Sí. Permite que los commands retornen diferentes tipos de resultado (éxito, éxito parcial, fallo) con información de error apropiada.

P: ¿Cómo implemento cleanup de recursos de command? R: Asegura cleanup apropiado de recursos (conexiones, archivos, memoria) después de ejecución de command. Usa bloques try-finally o gestores de recursos.

P: ¿Pueden los commands ejecutarse con lógica condicional? R: Sí. Añade ejecución condicional basada en estado de runtime, configuración o condiciones externas. Esto habilita comportamiento dinámico.

P: ¿Cómo implemento schemas de validación de command? R: Usa schemas (JSON Schema, librerías de validación) para validar parámetros de command. Esto asegura type safety e integridad de datos.

P: ¿Deberían los commands soportar múltiples niveles de undo? R: Sí. Implementa undo multinivel con un stack de historial de command. Esto permite a usuarios deshacer múltiples operaciones en secuencia.

P: ¿Cómo implemento formatos de serialización de command? R: Elige formatos de serialización apropiados (JSON, Protocol Buffers, Avro) basado en rendimiento, compatibilidad y requisitos de legibilidad.

P: ¿Pueden los commands ejecutarse con balanceo de carga? R: Sí. Distribuye ejecución de command across múltiples workers usando estrategias de balanceo de carga. Esto mejora escalabilidad y rendimiento.

P: ¿Cómo implemento health checks de command? R: Añade lógica de health check a commands para verificar su estado operacional. Esto es útil para monitoreo y mantenimiento.

P: ¿Deberían los commands soportar múltiples políticas de ejecución? R: Sí. Implementa diferentes políticas de ejecución (reintento, circuit break, timeout) basado en tipo de command y requisitos.

P: ¿Cómo implemento persistencia de estado de command? R: Persiste estado de command a almacenamiento durable para recuperación después de fallos. Esto asegura que commands en progreso puedan reanudarse.

P: ¿Pueden los commands ejecutarse con coordinación distribuida? R: Sí. Usa servicios de coordinación distribuida (ZooKeeper, etcd) para coordinar ejecución de command across múltiples nodos.

P: ¿Cómo implemento optimización de rendimiento de command? R: Perfila ejecución de command y optimiza cuellos de botella. Usa caché, procesamiento por lotes y ejecución paralela para mejorar rendimiento.

P: ¿Deberían los commands soportar múltiples estrategias de manejo de errores? R: Sí. Implementa diferentes estrategias de manejo de errores (reintento, fallback, ignorar) basado en tipo y severidad de error.

P: ¿Cómo implemento auditoría de seguridad de command? R: Logea todas las ejecuciones de command con usuario, timestamp y parámetros para auditoría de seguridad. Esto es importante para cumplimiento y forensia.

P: ¿Pueden los commands ejecutarse con pooling de recursos? R: Sí. Usa pools de recursos (conexiones de base de datos, pools de hilos) para mejorar eficiencia y reducir overhead de recursos.

P: ¿Cómo implemento estrategias de testing de command? R: Escribe pruebas unitarias para commands individuales y pruebas de integración para cadenas de command. Usa mocking para aislar dependencias.

P: ¿Deberían los commands soportar múltiples entornos de ejecución? R: Sí. Permite que los commands ejecuten en diferentes entornos (desarrollo, staging, producción) con configuración apropiada.

P: ¿Cómo implemento resolución de dependencias de command? R: Implementa resolución de dependencias para determinar automáticamente el orden de ejecución basado en dependencias de command. Esto simplifica workflows complejos.

P: ¿Pueden los commands ejecutarse con degradación graceful? R: Sí. Implementa degradación graceful donde los commands fallan a comportamiento alternativo cuando dependencias no están disponibles.

P: ¿Cómo implemento feature flags de command? R: Usa feature flags para habilitar o deshabilitar comportamiento de command sin cambios de código. Esto habilita experimentación y rollouts graduales.

P: ¿Deberían los commands soportar múltiples estrategias de caché de resultados? R: Sí. Implementa diferentes estrategias de caché (basado en tiempo, basado en tamaño, basado en invalidación) basado en características de command.

P: ¿Cómo implemento tracing distribuido de command? R: Añade tracing distribuido a commands para rastrear ejecución across boundaries de servicio. Esto mejora debugging y observabilidad.

P: ¿Pueden los commands ejecutarse con rate limiting por usuario? R: Sí. Implementa rate limiting por usuario para prevenir abuso y asegurar asignación justa de recursos.

P: ¿Cómo implemento compatibilidad backward de command? R: Diseña commands para ser backward compatibles soportando múltiples versiones y proporcionando paths de migración.

P: ¿Deberían los commands soportar múltiples prioridades de ejecución? R: Sí. Implementa colas de prioridad para asegurar que commands críticos se ejecuten antes que los menos importantes.

P: ¿Cómo implemento resolución de conflictos de estado de command? R: Implementa estrategias de resolución de conflictos (last-write-wins, merge, resolución manual) para ejecución concurrente de command.

P: ¿Pueden los commands ejecutarse con aislamiento de recursos? R: Sí. Usa aislamiento de recursos (contenedores, sandboxes) para limitar el impacto de fallos de command y mejorar seguridad.

P: ¿Cómo implemento load shedding de command? R: Implementa load shedding para rechazar commands cuando el sistema está sobrecargado. Esto previene fallos en cascada.

P: ¿Deberían los commands soportar múltiples timeouts de ejecución? R: Sí. Implementa diferentes valores de timeout para diferentes tipos de command basado en su tiempo de ejecución esperado.

P: ¿Cómo implemento versioning de estado de command? R: Versiona estado de command para soportar evolución de schema y compatibilidad backward. Esto es importante para sistemas de larga vida.

P: ¿Pueden los commands ejecutarse con locking distribuido? R: Sí. Usa locks distribuidos para prevenir ejecución concurrente de commands conflictivos across múltiples nodos.

P: ¿Cómo implemento monitoreo de rendimiento de command? R: Monitorea tiempo de ejecución de command, throughput y tasas de error. Usa estos datos para identificar problemas de rendimiento y optimizar.

P: ¿Deberían los commands soportar múltiples estrategias de agregación de resultados? R: Sí. Implementa diferentes estrategias de agregación (suma, promedio, conteo) basado en requisitos de command.

P: ¿Cómo implemento automatización de recuperación de errores de command? R: Automatiza recuperación de errores usando reintento, fallback y mecanismos de compensación. Esto reduce intervención manual.

P: ¿Pueden los commands ejecutarse con propagación de contexto? R: Sí. Propaga contexto (usuario, trace ID, metadata) across ejecución de command para observabilidad y debugging.

P: ¿Cómo implemento cuotas de recursos por usuario de command? R: Implementa cuotas de recursos por usuario para prevenir abuso y asegurar asignación justa de recursos.

P: ¿Deberían los commands soportar múltiples modos de ejecución? R: Sí. Soporta modos de ejecución síncrono, asíncrono y fire-and-forget basado en requisitos de caso de uso.

P: ¿Cómo implemento backup de estado de command? R: Backup estado de command antes de ejecución y restaura en fallo. Esto asegura recuperación de fallos.

P: ¿Pueden los commands ejecutarse con reintento condicional? R: Sí. Implementa reintento condicional basado en tipo de error. Esto mejora resiliencia para fallos transientes.

P: ¿Cómo implemento políticas de seguridad de command? R: Define y aplica políticas de seguridad para ejecución de command basado en roles de usuario y permisos.

P: ¿Deberían los commands soportar múltiples estrategias de validación de resultados? R: Sí. Valida resultados contra schemas, invariantes y reglas de negocio.

P: ¿Cómo implemento profiling de rendimiento de command? R: Perfila ejecución de command para identificar cuellos de botella y oportunidades de optimización.

P: ¿Pueden los commands ejecutarse con transacciones distribuidas? R: Sí. Usa protocolos de transacción distribuida (2PC, Saga) para asegurar atomicidad across múltiples servicios.

P: ¿Cómo implemento notificación de error de command? R: Envía notificaciones (email, Slack, PagerDuty) para fallos de command para habilitar respuesta oportuna.

P: ¿Deberían los commands soportar múltiples estrategias de ejecución? R: Sí. Implementa diferentes estrategias (directa, encolada, programada) basado en características de command.

P: ¿Cómo implemento reconciliación de estado de command? R: Reconcilia estado de command con sistemas externos para asegurar consistencia y detectar drift.

P: ¿Pueden los commands ejecutarse con reserva de recursos? R: Sí. Reserva recursos antes de ejecución de command para prevenir agotamiento de recursos.

P: ¿Cómo implemento baselines de rendimiento de command? R: Establece baselines de rendimiento para commands y alerta sobre desviaciones. Esto habilita gestión de rendimiento proactiva.

P: ¿Deberían los commands soportar múltiples estrategias de transformación de resultados? R: Sí. Transforma resultados a diferentes formatos basado en requisitos del consumidor.

P: ¿Cómo implemento clasificación de errores de command? R: Clasifica errores (transiente, permanente, reintentable) para determinar estrategias de manejo apropiadas.

P: ¿Pueden los commands ejecutarse con caché distribuido? R: Sí. Usa cachés distribuidos (Redis, Memcached) para compartir resultados de command across múltiples nodos.

P: ¿Cómo implemento migración de estado de command? R: Migra estado de command cuando los schemas cambian para asegurar compatibilidad backward.

P: ¿Deberían los commands soportar múltiples contextos de ejecución? R: Sí. Ejecuta en diferentes contextos con permisos y límites apropiados.

P: ¿Cómo implemento optimización de rendimiento de command? R: Optimiza a través de caché, procesamiento por lotes y ejecución paralela.

P: ¿Pueden los commands ejecutarse con shutdown graceful? R: Sí. Implementa shutdown graceful para completar commands en progreso antes de detener.

¿Es este patrón adecuado para proyectos pequeños?

Para proyectos pequeños con pocos componentes, este patrón puede añadir complejidad innecesaria. Empieza simple e introduce el patrón cuando sientas el problema que resuelve.

¿Cómo se compara este patrón con alternativas?

Cada patrón hace diferentes trade-offs. Revisa la tabla de variantes arriba y considera tus restricciones específicas: tamaño del equipo, requisitos de rendimiento y planes de escalado.

¿Puedo aplicar este patrón parcialmente?

Sí. Muchos equipos adoptan patrones incrementalmente. Empieza con la idea central y añade sofisticación según sea necesario. El patrón es una guía, no un blueprint estricto.

P: ¿Cómo implemento error recovery automation de command? R: Automatiza recuperación de errores usando reintento, fallback y mecanismos de compensación.

P: ¿Deberían los commands soportar múltiples formatos de resultado? R: Sí. Retorna resultados en diferentes formatos (JSON, XML, binario) basado en requisitos del cliente.

P: ¿Cómo implemento sincronización de estado de command? R: Sincroniza estado de command across sistemas distribuidos usando protocolos de consenso.

P: ¿Pueden los commands ejecutarse con aislamiento de recursos? R: Sí. Usa aislamiento de recursos para limitar el impacto de fallos de command.

P: ¿Cómo implemento monitoreo de rendimiento de command? R: Monitorea métricas de ejecución de command para optimización de rendimiento.

P: ¿Deberían los commands soportar múltiples prioridades de ejecución? R: Sí. Implementa colas de prioridad para ejecución crítica de command.

P: ¿Cómo implemento resolución de conflictos de estado de command? R: Resuelve conflictos de ejecución concurrente de command usando estrategias apropiadas.

P: ¿Pueden los commands ejecutarse con locking distribuido? R: Sí. Usa locks distribuidos para prevenir ejecución conflictiva de command.

P: ¿Cómo implemento load shedding de command? R: Rechaza commands cuando el sistema está sobrecargado para prevenir fallos.

P: ¿Deberían los commands soportar múltiples timeouts de ejecución? R: Sí. Implementa diferentes timeouts basados en tipo de command.

P: ¿Cómo implemento versioning de estado de command? R: Versiona estado de command para soportar evolución de schema.

P: ¿Pueden los commands ejecutarse con propagación de contexto? R: Sí. Propaga contexto across ejecución de command para observabilidad.

P: ¿Cómo implemento cuotas de recursos de command? R: Implementa cuotas de recursos para prevenir agotamiento de recursos.

P: ¿Deberían los commands soportar múltiples modos de ejecución? R: Sí. Soporta diferentes modos basados en requisitos.

P: ¿Cómo implemento backup de estado de command? R: Backup estado de command para recuperación de fallos.

P: ¿Pueden los commands ejecutarse con reintento condicional? R: Sí. Implementa reintento condicional basado en tipo de error.

P: ¿Cómo implemento políticas de seguridad de command? R: Aplica políticas de seguridad para ejecución de command.

P: ¿Deberían los commands soportar múltiples estrategias de validación de resultados? R: Sí. Valida resultados contra schemas y reglas de negocio.

P: ¿Cómo implemento profiling de rendimiento de command? R: Perfila ejecución de command para identificar cuellos de botella.

P: ¿Pueden los commands ejecutarse con transacciones distribuidas? R: Sí. Usa protocolos de transacción distribuida para atomicidad.

P: ¿Cómo implemento notificación de error de command? R: Envía notificaciones para fallos de command.

P: ¿Deberían los commands soportar múltiples estrategias de ejecución? R: Sí. Implementa diferentes estrategias basadas en tipo de command.

P: ¿Cómo implemento reconciliación de estado de command? R: Reconcilia estado con sistemas externos para consistencia.

P: ¿Pueden los commands ejecutarse con reserva de recursos? R: Sí. Reserva recursos antes de ejecución de command.

P: ¿Cómo implemento baselines de rendimiento de command? R: Establece baselines y alerta sobre desviaciones.

P: ¿Deberían los commands soportar múltiples estrategias de transformación de resultados? R: Sí. Transforma resultados basado en requisitos del consumidor.

P: ¿Cómo implemento clasificación de errores de command? R: Clasifica errores para determinar estrategias de manejo.

P: ¿Pueden los commands ejecutarse con caché distribuido? R: Sí. Usa cachés distribuidos para compartir resultados.

P: ¿Cómo implemento migración de estado de command? R: Migra estado cuando los schemas cambian.

P: ¿Deberían los commands soportar múltiples contextos de ejecución? R: Sí. Ejecuta en diferentes contextos con permisos apropiados.

P: ¿Cómo implemento optimización de rendimiento de command? R: Optimiza a través de caché, procesamiento por lotes y ejecución paralela.

P: ¿Pueden los commands ejecutarse con shutdown graceful? R: Sí. Implementa shutdown graceful para commands en progreso.