MCPcopy Index your code
hub / github.com/bertilmuth/requirementsascode

github.com/bertilmuth/requirementsascode @v1.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.1 ↗ · + Follow
1,016 symbols 5,295 edges 156 files 151 documented · 15%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

requirements as code

Build Status

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.

getting started

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'

how to use requirements as code

Here's what you need to do as a developer:

Step 1: Build a model defining the event classes to handle, and the methods that react to events:

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.

Step 2: Create a runner and run the model:

ModelRunner runner = new ModelRunner().run(model);

Step 3: Send events to the runner, and enjoy watching it react:

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.

hello world

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;
        }
    }
}

event queue for non-blocking handling

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.

publishing events

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.

documentation

publications

subprojects

build from sources

Use Java >=11 and the project's gradle wrapper to build from sources.

related topics

  • The work of Ivar Jacobson on Use Cases. As an example, have a look at Use Case 2.0.
  • The work of Alistair Cockburn on Use Cases, specifically the different goal levels. Look here to get started, or read the book "Writing Effective Use Cases".

Extension points exported contracts — how you extend this code

DomainEvent (Interface)
Based on code by Jakub Pilimon: https://gitlab.com/pilloPl/eventsourced-credit-cards/blob/4329a0aac283067f1376b3802e13f5 [8 …
requirementsascodeexamples/creditcard_eventsourcing/src/main/java/creditcard_eventsourcing/model/DomainEvent.java
IObtainPoems (Interface)
Driven, right side port for obtaining poems, e.g. from a repository outside the hexagon. Inspired by a talk by A. Cockb [4 …
requirementsascodeexamples/hexagon/src/main/java/hexagon/port/IObtainPoems.java
Condition (Interface)
(no doc) [16 implementers]
requirementsascodecore/src/main/java/org/requirementsascode/Condition.java
Display (Interface)
(no doc) [4 implementers]
requirementsascodeexamples/shoppingappjavafx/src/main/java/shoppingappjavafx/usecaserealization/componentinterface/Display.java
IWriteLines (Interface)
Driven, right side port for writing the lines of a poem to an output device outside the hexagon, e.g. the console. Insp [2 …
requirementsascodeexamples/hexagon/src/main/java/hexagon/port/IWriteLines.java

Core symbols most depended-on inside this repo

system
called by 329
requirementsascodecore/src/main/java/org/requirementsascode/StepPart.java
step
called by 319
requirementsascodecore/src/main/java/org/requirementsascode/FlowPart.java
user
called by 194
requirementsascodecore/src/main/java/org/requirementsascode/StepPart.java
useCase
called by 170
requirementsascodecore/src/main/java/org/requirementsascode/ModelBuilder.java
build
called by 148
requirementsascodecore/src/main/java/org/requirementsascode/UseCasePart.java
basicFlow
called by 130
requirementsascodecore/src/main/java/org/requirementsascode/UseCasePart.java
reactTo
called by 128
requirementsascodecore/src/main/java/org/requirementsascode/ModelRunner.java
as
called by 107
requirementsascodecore/src/main/java/org/requirementsascode/StepPart.java

Shape

Method 838
Class 173
Interface 5

Languages

Java100%

Modules by API surface

requirementsascodecore/src/test/java/org/requirementsascode/FlowTest.java59 symbols
requirementsascodecore/src/test/java/org/requirementsascode/BuildModelTest.java39 symbols
requirementsascodeexamples/creditcard_eventsourcing/src/main/java/creditcard_eventsourcing/model/CreditCard.java33 symbols
requirementsascodecore/src/main/java/org/requirementsascode/ModelRunner.java33 symbols
requirementsascodecore/src/main/java/org/requirementsascode/UseCasePart.java26 symbols
requirementsascodecore/src/test/java/org/requirementsascode/AbstractTestCase.java25 symbols
requirementsascodeexamples/shoppingappjavafx/src/main/java/shoppingappjavafx/domain/ShippingInformation.java22 symbols
requirementsascodecore/src/test/java/org/requirementsascode/FlowlessTest.java21 symbols
requirementsascodeexamples/creditcard_eventsourcing/src/test/java/creditcard_eventsourcing/model/CreditCardModelRunnerTest.java17 symbols
requirementsascodeexamples/helloworld/src/main/java/helloworld/HelloWorld06.java16 symbols
requirementsascodecore/src/main/java/org/requirementsascode/Model.java16 symbols
requirementsascodeexamples/shoppingappjavafx/src/main/java/shoppingappjavafx/usecaserealization/BuyProductRealization.java15 symbols

For agents

$ claude mcp add requirementsascode \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact