saga-lib
Java saga library to organize domain events. The goal is to organize state and
activities triggered by any sort of messages. An example of what this actually
means can be found in the wiki.
This lib makes it easy for a developer to focus on the actual state changes and
message handling. To do this the persistence of state as well as the possible
escalation using timeouts is separated from the actual business code.
Add Reference to saga-lib
It is recommended to use Maven to reference the saga-lib binaries.
<dependency>
<groupId>com.codebullets.saga-lib</groupId>
<artifactId>saga-lib</artifactId>
<version>3.4.0</version>
</dependency>The binaries and code are also available for download from the Maven Central
repository.
Starting the saga lib
The lib is started using the provided builder class. It will use default
implementations for things like persistence and timeouts if not specified
otherwise. The build() method returns an instance ready to be used.
MessageStream msgStream = EventStreamBuilder.configure()
.usingSagaProviderFactory(sagaProvider)
.build();The above code will create a MessageStream instance. Use this instance to
bring messages into the lib. The object sagaProvider from the example above
is responsible to return individual JSR-330 providers used to request new
instances of the respective saga type. Guice as well as Spring both support
JSR-330, so it is quite easy to create a custom provider.
The above example will persist the state of the saga in memory, timeouts will
be triggered based on Java timers and available Sagas will be automatically
determined by scanning the current classpath. All of this behaviour can be
customized by providing own implementations and calling one of the using
methods.
A saga may look like the example below. The messages handled are POJOs holding
only the event data. They can be of any object type. The MySagaState type is
expected to be a POJO as well implementing the SagaState interface. The
method starting the saga is indicated by the @StartsSaga annotation while all
further handlers need to be annotated with @eventhandler. There can only be
one message starting a saga. There is no limit to the number of further
event handlers.
This saga is using annotations to define the handlers, but there is also an
option to use a direct Fluent API api.
public class MySaga extends AbstractSaga<MySagaState> {
@StartsSaga
public void sagaStartup(StartingMessage startedByMessage) {
// perform saga start logic
// save id to map further messages back to the specific saga state
state().addInstanceKey(startedByMessage.getId());
requestTimeout(60, TimeUnit.SECONDS);
}
@EventHandler
public void continueSagaWithOtherMessage(OtherMessage message) {
// perform custom logic and mark saga as finished
setFinished();
}
@EventHandler
public void handleTimeout(Timeout timeout) {
// perform timeout logic
// timeout finishes saga therefore call setFinished()
setFinished();
}
@Override
public void createNewState() {
setState(new MySagaState());
}
@Override
public Collection<KeyReader> keyReaders() {
// creates a reader used to map between the OtherMessage event
// and the stored instance key of the existing saga state.
KeyReader reader = KeyReaders.forMessage(
OtherMessage.class,
message -> message.getId());
return Lists.newArrayList(reader);
}
}