This project simplifies the development of event-driven applications.
It provides a concise way to create handlers for many types of events at once. A single runner receives events, and dispatches them to handlers. When activated, the runner also records the events. That can be used for replay in event sourced applications.
You can also customize the event handler in a simple way, for example for measuring performance, or for logging purposes.
For more advanced scenarios that depend on the application's state, you can create a use case model with flows. It's a simple alternative to state machines, understandable by developers and business people alike.
For the long term maintenance of your application, you can generate documentation from the models inside the code without the need to add comments to it.
At least Java 8 is required to use requirements as code, download and install it if necessary.
Requirements as code is available on Maven Central.
If you are using Maven, include the following in your POM, to use the core:
<dependency>
<groupId>org.requirementsascode</groupId>
<artifactId>requirementsascodecore</artifactId>
<version>1.1</version>
</dependency>
If you are using Gradle, include the following in your build.gradle, to use the core:
compile 'org.requirementsascode:requirementsascodecore:1.1'
Here's what you need to do as a developer:
Model model = Model.builder()
.on(<event class>).system(<lambda expression, or reference to method that handles event>)
.on(..).system(...)
...
.build();
The order of the statements has no significance.
For handling exceptions instead of events, use the specific exception's class or Throwable.class.
Use condition before on to define an additional precondition that must be fulfilled.
You can also use condition without on, meaning: execute at the beginning of the run, or after a step has been run,
if the condition is fulfilled.
ModelRunner runner = new ModelRunner().run(model);
runner.reactTo(<Event POJO Object> [, <Event POJO Object>,...]);
If an event's class is not declared in the model, the runner consumes it silently. If an unchecked exception is thrown in one of the handler methods and it is not handled by any other handler method, the runner will rethrow it.
Here's a complete Hello World example:
package hello;
import org.requirementsascode.Model;
import org.requirementsascode.ModelRunner;
public class HelloUser {
public static void main(String[] args) {
Model model = Model.builder()
.on(DisplayHelloRequested.class).system(HelloUser::displayHello)
.on(NameEntered.class).system(HelloUser::displayEnteredName)
.build();
new ModelRunner().run(model)
.reactTo(new DisplayHelloRequested(), new NameEntered("Joe"));
}
public static void displayHello(DisplayHelloRequested displayHelloRequested) {
System.out.println("Hello!");
}
public static void displayEnteredName(NameEntered nameEntered) {
System.out.println("Welcome, " + nameEntered.getUserName() + ".");
}
static class DisplayHelloRequested {}
static class NameEntered {
private String userName;
public NameEntered(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
}
}
The default mode for the ModelRunner is to handle events in a blocking way. Instead, you can use a simple event queue that processes events one by one in its own thread:
Model model = ...;
ModelRunner modelRunner = new ModelRunner();
modelRunner.run(model);
EventQueue queue = new EventQueue(modelRunner::reactTo);
queue.put(new String("I'm an event, react to me!"));
The constructor argument of EventQueue specifies that each event that's put() will be placed in the queue, and then forwarded to ModelRunner.reactTo().
Note that you can forward events to any other consumer of an object as well.
When you use the system() method, you are restricted to just consuming events.
But ýou can also publish events with systemPublish(), like so:
Model model = modelBuilder
.useCase("Use Case UC1")
.basicFlow()
.step("S1").on(EntersText.class).systemPublish(this::publishEnteredTextAsString)
.build();
...
private String[] publishEnteredTextAsString(EnteredText enteredText) {
return new String[] { enteredText.value() };
}
As you can see, the publishing method has an event object as input parameter, and returns an object array of events to be published (not necessarily strings).
By default, the model runner takes the returned events and sends them to its own reactTo() method.
This behavior can be overriden by specifying a custom event handler with publishWith().
For example, you can use modelRunner.publishWith(queue::put) to publish the events to an event queue.
Use Java >=11 and the project's gradle wrapper to build from sources.
$ claude mcp add requirementsascode \
-- python -m otcore.mcp_server <graph>