I just released version v2.0 of the requirements as code library.
Using it, you can create a single behavior that receives messages from the calling code. The behavior then dispatches the messages to an appropriate message handler. Which handler is appropriate is defined in a behavior model.
In the following example, a user sends a request with the user name (“Joe”). The system greets the user with “Hello, Joe.”
package helloworld;
import java.util.function.Consumer;
import org.requirementsascode.Behavior;
import org.requirementsascode.BehaviorModel;
import org.requirementsascode.Model;
import org.requirementsascode.StatelessBehavior;
public class HelloUser {
public static void main(String[] args) {
GreeterModel greeterModel = new GreeterModel(HelloUser::sayHello);
Behavior greeter = StatelessBehavior.of(greeterModel);
greeter.reactTo(new SayHelloRequest("Joe"));
}
private static void sayHello(SayHelloRequest requestsHello) {
System.out.println("Hello, " + requestsHello.getUserName() + ".");
}
}
class GreeterModel implements BehaviorModel {
private final Consumer<SayHelloRequest> sayHello;
public GreeterModel(Consumer<SayHelloRequest> sayHello) {
this.sayHello = sayHello;
}
@Override
public Model model() {
Model model = Model.builder()
.user(SayHelloRequest.class).system(sayHello)
.build();
return model;
}
}
class SayHelloRequest {
private final String userName;
public SayHelloRequest(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
}
Since the behavior is the central point of control for all functions, you can inject and configure the dependencies of all functions through it. That makes it easy to implement a hexagonal architecture or clean architecture.
The following example shows how to implement a clean architecure with requirements as code, in principle:
public class CleanArchitectureOutline {
public static void main(String[] args) {
ConsolePrinter consolePrinter = new ConsolePrinter();
GreetingServiceModel greetingServiceModel = new GreetingServiceModel(consolePrinter);
Behavior greetingService = StatelessBehavior.of(greetingServiceModel);
greetingService.reactTo(new SayHelloRequest("Joe"));
}
}
/**
* The behavior model defines that a consumer reacts to SayHelloRequest.
*
* @author b_muth
*
*/
class GreetingServiceModel implements BehaviorModel {
private final Consumer<String> outputPort;
public GreetingServiceModel(Consumer<String> outputPort) {
this.outputPort = outputPort;
}
@Override
public Model model() {
Model model = Model.builder()
.user(SayHelloRequest.class).system(sayHello())
.build();
return model;
}
private SayHello sayHello() {
return new SayHello(outputPort);
}
}
/**
* Command class
*/
class SayHelloRequest {
private final String userName;
public SayHelloRequest(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
}
/**
* Message handler
*/
class SayHello implements Consumer<SayHelloRequest> {
private final Consumer<String> outputPort;
public SayHello(Consumer<String> outputPort) {
this.outputPort = outputPort;
}
public void accept(SayHelloRequest requestHello) {
String greeting = Greeting.forUser(requestHello.getUserName());
outputPort.accept(greeting);
}
}
/**
* Infrastructure class
*/
class ConsolePrinter implements Consumer<String>{
public void accept(String message) {
System.out.println(message);
}
}
/**
* Domain class
*/
class Greeting {
public static String forUser(String userName) {
return "Hello, " + userName + ".";
}
}
I think about writing an article about a new, modern approach to clean architecture soon. My goal is to further simplify the development of a service with a clean architecture in a Spring environment.
Are you interested? Tell me what you think in the comments.