All Known Implementing Classes:
AbstractCommandBus, LocalCommandBus

public interface CommandBus

CommandBus / LocalCommandBus / DurableLocalCommandBus

The CommandBus pattern decouples the sending of commands from their handling by introducing an indirection between the command and its corresponding CommandHandler. This design provides location transparency, meaning that the sender does not need to know which handler will process the command. The LocalCommandBus is the default implementation of the CommandBus pattern.

Single CommandHandler Requirement

Command Processing and Return Values

  • No Expected Return Value: According to CQRS principles, command handling typically does not return a value.
  • Optional Return Value: The API allows a CommandHandler to return a value if necessary (for example, a server-generated ID).

Sending Commands

Commands can be dispatched in different ways depending on your needs:

1. Synchronous processing

  • Method: send(Object)
  • Usage: Use this when you need to process a command immediately and receive instant feedback on success or failure.

2. Asynchronous processing with Feedback

  • Method: sendAsync(Object)
  • Usage: Returns a Mono that will eventually contain the result of the command processing. This is useful when you want asynchronous processing but still need to know the outcome.

3. Fire-and-Forget Asynchronous processing

  • Method: sendAndDontWait(Object) or sendAndDontWait(Object, java.time.Duration)
  • Usage: Sends a command without waiting for a result. This is true fire-and-forget asynchronous processing.
  • Note: If durability is required (ensuring the command is reliably processed), consider using DurableLocalCommandBus from the essentials-components's foundation module instead.

Handling Command Processing Failures

Using send(Object) / sendAsync(Object)

  • Immediate Error Feedback: These methods provide quick feedback if command processing fails (e.g., due to business validation errors), making error handling straightforward.

Using sendAndDontWait(Object)

  • Delayed Asynchronous Processing: Commands sent via this method are processed in a true asynchronous fire-and-forget fashion, with an optional delay.
  • Spring Boot Starters Default: When using the Essentials Spring Boot starters, the default CommandBus implementation is DurableLocalCommandBus, which places sendAndDontWait(Object) commands on a DurableQueue for reliable processing.
  • Exception Handling: Because processing is asynchronous fire-and-forget (and because commands may be processed on a different node or after a JVM restart), exceptions can not be captured by the caller.
  • Error Management: The SendAndDontWaitErrorHandler takes care of handling exceptions that occur during asynchronous command processing.
  • Retries: The DurableLocalCommandBus is configured with a RedeliveryPolicy to automatically retry commands that fail.

Summary

  • For Immediate Feedback: Use send(Object) or sendAsync(Object) to simplify error handling with instant feedback on command processing.
  • For True Asynchronous, Fire-and-Forget Processing: Use sendAndDontWait(Object) when you require asynchronous processing with retry capabilities.
  • Be aware that error handling is deferred to the SendAndDontWaitErrorHandler and managed by any configured RedeliveryPolicy.

Example:


  var commandBus = new LocalCommandBus();
  commandBus.addCommandHandler(new CreateOrderCommandHandler(...));
  commandBus.addCommandHandler(new ImburseOrderCommandHandler(...));

  var optionalResult = commandBus.send(new CreateOrder(...));
  // or
  var monoWithOptionalResult = commandBus.sendAsync(new ImbuseOrder(...))
                                         .block(Duration.ofMillis(1000));
 
 

In case you need to colocate multiple related command handling methods inside a single class then you should have your command handling class extend AnnotatedCommandHandler.
Example:


 public class OrdersCommandHandler extends AnnotatedCommandHandler {

      @Handler
      private OrderId handle(CreateOrder cmd) {
         ...
      }

      @Handler
      private void someMethod(ReimburseOrder cmd) {
         ...
      }
 }
See Also:
  • Method Details

    • getInterceptors

      List<CommandBusInterceptor> getInterceptors()
      Returns an immutable list of interceptors
      Returns:
      Returns an immutable list of interceptors
    • addInterceptor

      CommandBus addInterceptor(CommandBusInterceptor interceptor)
      Add a new interceptor
      Parameters:
      interceptor - the interceptor to add
      Returns:
      this bus instance
    • addInterceptors

      default CommandBus addInterceptors(List<CommandBusInterceptor> interceptors)
      Add new interceptors
      Parameters:
      interceptors - the interceptors to add
      Returns:
      this bus instance
    • hasInterceptor

      boolean hasInterceptor(CommandBusInterceptor interceptor)
      Guard method to test if the CommandBus already contains the interceptor
      Parameters:
      interceptor - the interceptor to check
      Returns:
      true if the CommandBus already contains the interceptor, otherwise false
    • removeInterceptor

      CommandBus removeInterceptor(CommandBusInterceptor interceptor)
      Remove an interceptor
      Parameters:
      interceptor - the interceptor to remove
      Returns:
      this bus instance
    • addCommandHandler

      CommandBus addCommandHandler(CommandHandler commandHandler)
      Add a new command handler.
      Parameters:
      commandHandler - the command handler to add
      Returns:
      this bus instance
    • removeCommandHandler

      CommandBus removeCommandHandler(CommandHandler commandHandler)
      Remove a command handler.
      Parameters:
      commandHandler - the command handler to remove
      Returns:
      this bus instance
    • send

      <R, C> R send(C command)
      The send command synchronously and process the command on the sending thread
      Type Parameters:
      R - the return type
      C - the command type
      Parameters:
      command - the command to send
      Returns:
      the result of the command processing (if any)
      Throws:
      MultipleCommandHandlersFoundException - If multiple CommandHandler claim that they can handle a given command
      NoCommandHandlerFoundException - If no CommandHandler's can handle the given command
    • sendAsync

      <R, C> reactor.core.publisher.Mono<R> sendAsync(C command)
      The send command asynchronously and process the command on a Schedulers.boundedElastic() thread
      Type Parameters:
      R - the return type
      C - the command type
      Parameters:
      command - the command to send
      Returns:
      a Mono that will contain the result of the command processing (if any)
      Throws:
      MultipleCommandHandlersFoundException - If multiple CommandHandler claim that they can handle a given command
      NoCommandHandlerFoundException - If no CommandHandler's can handle the given command
    • sendAndDontWait

      <C> void sendAndDontWait(C command)
      The send command asynchronously without waiting for the result of processing the Command.
      The command handler cannot return any value when invoked using this method.
      Type Parameters:
      C - the command type
      Parameters:
      command - the command to send
      Throws:
      MultipleCommandHandlersFoundException - If multiple CommandHandler claim that they can handle a given command
      NoCommandHandlerFoundException - If no CommandHandler's can handle the given command
    • sendAndDontWait

      <C> void sendAndDontWait(C command, Duration delayMessageDelivery)
      The send command asynchronously without waiting for the result of processing the Command.
      The command handler cannot return any value when invoked using this method.
      Type Parameters:
      C - the command type
      Parameters:
      command - the command to send
      delayMessageDelivery - how long should we delay the command message sending
      Throws:
      MultipleCommandHandlersFoundException - If multiple CommandHandler claim that they can handle a given command
      NoCommandHandlerFoundException - If no CommandHandler's can handle the given command
    • findCommandHandlerCapableOfHandling

      CommandHandler findCommandHandlerCapableOfHandling(Object command)
      Find a CommandHandler capable of handling the given command
      Parameters:
      command - the command to handle
      Returns:
      the single CommandHandler that's capable of handling the given command
      Throws:
      MultipleCommandHandlersFoundException - If multiple CommandHandler claim that they can handle a given command
      NoCommandHandlerFoundException - If no CommandHandler's can handle the given command
    • hasCommandHandler

      boolean hasCommandHandler(CommandHandler commandHandler)
      Guard method to test if the CommandBus already contains the commandHandler
      Parameters:
      commandHandler - the commandHandler to check
      Returns:
      true if the CommandBus already contains the commandHandler, otherwise false