CDI/Spring enabled Java Command-Bus
Command
- Marker InterfaceCommandHandler
- One Implementation perCommand
. Provideshandle(CommandImplementation)
Method.CommandBus
- Finds and calls theCommandHandler
for eachCommand
.CommandBus
can be decorated, in order to implement cross-cutting concerns, such as logging, transaction handling, validation, autorization, metrics etc.
Add the latest stable version of command-bus to the dependency management tool of your choice.
You can also get snapshot versions from our snapshot repository (for the most recent commit on develop branch).
To do so, add the following repo to your pom.xml
or settings.xml
:
<repository>
<id>snapshots-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases><enabled>false</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
There are different versions of command-bus for either CDI or spring.
<dependency>
<groupId>com.cloudogu.cb</groupId>
<artifactId>command-bus-cdi</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>com.cloudogu.cb</groupId>
<artifactId>command-bus-spring</artifactId>
<version>1.0.1</version>
</dependency>
- Bootstrapping
- CDI: Having the
command-bus-cdi
dependency on the classpath triggers the CDI extension - Spring: All CommandHandlers must be within the application context (e.g.
@Component
in spring boot)
- CDI: Having the
- Implement your
Command
s and the logic in appropriateCommandHandler
s. - You can now just inject the
CommandBus
and pass yourCommands
to itsexecute()
method. It will automatically pass it to the appropriate handler. - Examples:
- If you want to decorate your command bus (for logging, metrics, etc.), a factory/producer for the
CommandBus
is the central place where decorators can be instantiated. It brings together yourCommandBus
(e.g.CDICommandBus
,SpringCommandBus
) with decorators (see bellow). ExampleCommandBusFactory
s:
The CommandHandler
s for CDI and Spring both use a Registry
(CDI /
Spring) to store Command
s and
CommandHandler
s. Difference:
- CDI: The
CDIExtension
finds allCommand
s andCommandHandler
s and puts them on theRegistry
. - Spring: The
Registry
itself gets allCommand
s andCommandHandler
s from the application context.
First example is the logging decorator (LoggingCommandBus
) that logs entering and leaving (including time of execution) of CommandHandler
s.
The Command Bus provides two Prometheus metrics decorators. More information on Prometheus can be found on the
project's website.
In order to use them, make sure to provide the io.prometheus:simpleclient
dependency on the classpath.
The PrometheusMetricsCountingCommandBus
counts every executed command, using a Prometheus Counter.
The counter to be used must be provided as a constructor parameter. For each type of command (i.e. it's class name) a
label is created automatically.
The PrometheusMetricsTimingCommandBus
captures the time a command's execution takes and provides the metric as a
Prometheus Histogram. Similarly to the PrometheusMetricsCountingCommandBus
, the Histogram needs to be provided as a
constructor parameter.
The Command Bus provides two Micrometer metrics decorators. More information on Micrometer can be found on the
project's website.
In order to use them, make sure to provide a micrometer registry implementation such as prometheus io.micrometer:micrometer-registry-prometheus
.
See cloudogu/springboot-micrometer-demo-command-bus for a complete example.
The MicrometerCountingCommandBus
counts every executed command, using a Micrometer Counter e.g.:
CommandBus commandBusImpl = ...;
MicrometerCountingCommandBus commandBus = new MicrometerCountingCommandBus(commandBusImpl,
commandClass -> Counter.builder("command.counter")
.description("command execution counter")
.tags("command", commandClass.getSimpleName())
.register(Metrics.globalRegistry)
);
The MicrometerTimingCommandBus
measures the elapsed time for every command execution by using a Micrometer a Micrometer Counter e.g.:
CommandBus commandBusImpl = ...;
MicrometerTimingCommandBus commandBus = new MicrometerTimingCommandBus(commandBusImpl,
commandClass -> Timer.builder("command.timer")
.description("command execution timer")
.tags("command", commandClass.getSimpleName())
.register(Metrics.globalRegistry)
);
The ValidatingCommandBus
uses the javax.validation
API to validate the command, before execution.
The CommandBus
will throw an ConstraintViolationException
, if the command violates validation rules.
Make sure to provide javax.validation
implementation at runtime, such as org.hibernate:hibernate-validator
.
public class NotifyCommand implements Command<Void> {
@Email
private String email;
public SampleCommand(String email) {
this.email = email;
}
}
CommandBus commandBusImpl = ...;
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
ValidatingCommandBus commandBus = new ValidatingCommandBus(commandBusImpl, factory.getValidator());
NotifyCommand command = createNotifyCommand();
commandBus.execute(command);
Command
s can specify return values. SeeHelloCommand
andcom.cloudogu.cb.EchoCommandHandler
for example.- If you don't want a return value, use
Void
. SeeByeCommand
andByeCommandHandler
for example.