Como enviar un comando

Tres pasos: definir el payload, construir el comando, registrarlo en el outbox. Igual que con eventos, el outbox garantiza que el envio es atomico con el resto de la transaccion del caso de uso (si la TX revierte, el comando no se emite).

A diferencia de un evento de dominio (que es broadcast — varios suscriptores pueden reaccionar), un comando es dirigido a un servicio (y tenant) especifico. Y a diferencia de un evento, no hay un agregado donde acumularlo: el caso de uso llama al CommandRegistry directamente.

1. Definir el payload

Un record inmutable. Por convencion vive cerca del caso de uso que lo origina:

package com.example.task.usecase;

public record ArchiveTaskCommand(String taskId, String reason) {
}

2. Construir el comando

Command.toSelf(…​) construye un comando dirigido al mismo tenant (intra-tenant). Para inter-tenant usa Command.to(targetTenantId, …​):

import com.example.commons.messaging.command.Command;

Command<ArchiveTaskCommand> cmd = Command.toSelf(
        "task-service",
        new ArchiveTaskCommand(taskId, "expired")
);

sourceTenantId se resuelve automaticamente del TenantContextHolder actual. El commandType lo deriva del FQN de la clase del payload. Para el detalle de los campos ver Referencia: Command.

3. Registrarlo en el outbox

Inyecta CommandRegistry (output port) y llama register desde un metodo @Transactional. La anotacion garantiza que el INSERT en outbox_commands se commitea junto con el resto del caso de uso — si la TX revierte, el comando no se emite:

@Service
@RequiredArgsConstructor
class TaskService implements ArchiveTaskUseCase {

    private final CommandRegistry commandRegistry;

    @Transactional
    @Override
    public void archive(String taskId) {
        Command<ArchiveTaskCommand> cmd = Command.toSelf(
                "task-service",
                new ArchiveTaskCommand(taskId, "expired")
        );
        commandRegistry.register(cmd);
    }

}

CommandRegistry.register rutea por direction: los comandos creados con Command.to/toSelf van como OUTBOUND y caen en outbox_commands. El OutboxCommandDispatcher (background) los publica al transport en un tick posterior.

Activar el dispatcher de outbox

El dispatcher que saca los comandos del outbox y los publica al transport viene apagado por default. Habilitalo en application.yaml:

app:
  commands:
    outbox-dispatcher:
      enabled: true

Para los demas parametros y defaults ver Referencia: propiedades de mensajeria.

Verificar que salio

curl http://localhost:8080/actuator/message-boxes muestra el conteo de commands.outbox. Para detalle del endpoint ver Inspeccionar las bandejas de mensajeria.

Ver tambien