All Projects → bertilmuth → Requirementsascode

bertilmuth / Requirementsascode

Licence: apache-2.0
Translate use cases to code to improve long term maintenance.

Programming Languages

java
68154 projects - #9 most used programming language

Projects that are alternatives of or similar to Requirementsascode

Ray
项目停止更新,新项目:https://github.com/RayTale/Vertex
Stars: ✭ 635 (+388.46%)
Mutual labels:  event-sourcing, event-driven
Foxoffice
Sample application demonstrating how to build a distributed cloud .NET Core application based on CQRS and Event Sourcing.
Stars: ✭ 33 (-74.62%)
Mutual labels:  event-sourcing, event-driven
Microservices Event Sourcing
Microservices Event Sourcing 是一个微服务架构的在线购物网站,使用Spring Boot、Spring Cloud、Spring Reactor、OAuth2、CQRS 构建,实现了基于Event Sourcing的最终一致性,提供了构建端到端微服务的最佳实践
Stars: ✭ 657 (+405.38%)
Mutual labels:  event-sourcing, event-driven
Event Source Cqrs Sample
Sample ES/CQRS application
Stars: ✭ 380 (+192.31%)
Mutual labels:  event-sourcing, event-driven
Rails Disco
Distributed Rails with commands, events and projections.
Stars: ✭ 95 (-26.92%)
Mutual labels:  event-sourcing, event-driven
Run Aspnetcore Microservices
Microservices on .Net platforms which used Asp.Net Web API, Docker, RabbitMQ, MassTransit, Grpc, Ocelot API Gateway, MongoDB, Redis, PostgreSQL, SqlServer, Dapper, Entity Framework Core, CQRS and Clean Architecture implementation. Also includes Cross-Cutting concerns like Implementing Centralized Distributed Logging with Elasticsearch, Kibana and SeriLog, use the HealthChecks with Watchdog, Implement Retry and Circuit Breaker patterns with Polly and so on.. See Microservices Architecture and Step by Step Implementation on .NET Course w/ discount->
Stars: ✭ 406 (+212.31%)
Mutual labels:  event-sourcing, event-driven
Rails event store
A Ruby implementation of an Event Store based on Active Record
Stars: ✭ 947 (+628.46%)
Mutual labels:  event-sourcing, event-driven
ftgogo
FTGOGO - event-driven architecture demonstration application using edat
Stars: ✭ 82 (-36.92%)
Mutual labels:  event-sourcing, event-driven
Message Db
Microservice native message and event store for Postgres
Stars: ✭ 1,260 (+869.23%)
Mutual labels:  event-sourcing, event-driven
Event Sourcing Castanha
An Event Sourcing service template with DDD, TDD and SOLID. It has High Cohesion and Loose Coupling, it's a good start for your next Microservice application.
Stars: ✭ 68 (-47.69%)
Mutual labels:  event-sourcing, event-driven
eventide-postgres
Event Sourcing and Microservices Stack for Ruby
Stars: ✭ 92 (-29.23%)
Mutual labels:  event-sourcing, event-driven
Neventlite
NEventLite - An extensible lightweight library for .NET that manages the Aggregate lifecycle in an Event Sourced system. Supports Event and Snapshot storage providers like EventStore/Redis or SQL Server. Built with dependency injection in mind and seamlessly integrates with AspNetCore.
Stars: ✭ 117 (-10%)
Mutual labels:  event-sourcing, event-driven
reacted
Actor based reactive java framework for microservices in local and distributed environment
Stars: ✭ 17 (-86.92%)
Mutual labels:  event-sourcing, event-driven
Akkatecture
a cqrs and event sourcing framework for dotnet core using akka.net
Stars: ✭ 414 (+218.46%)
Mutual labels:  event-sourcing, event-driven
dudulina
CQRS + Event Sourcing library for PHP
Stars: ✭ 53 (-59.23%)
Mutual labels:  event-sourcing, event-driven
Pitstop
This repo contains a sample application based on a Garage Management System for Pitstop - a fictitious garage. The primary goal of this sample is to demonstrate several software-architecture concepts like: Microservices, CQRS, Event Sourcing, Domain Driven Design (DDD), Eventual Consistency.
Stars: ✭ 708 (+444.62%)
Mutual labels:  event-sourcing, event-driven
commander
Build event-driven and event streaming applications with ease
Stars: ✭ 60 (-53.85%)
Mutual labels:  event-sourcing, event-driven
incubator-eventmesh
EventMesh is a dynamic event-driven application runtime used to decouple the application and backend middleware layer, which supports a wide range of use cases that encompass complex multi-cloud, widely distributed topologies using diverse technology stacks.
Stars: ✭ 939 (+622.31%)
Mutual labels:  event-sourcing, event-driven
Asombroso Ddd
Una lista cuidadosamente curada de recursos sobre Domain Driven Design, Eventos, Event Sourcing, Command Query Responsibility Segregation (CQRS).
Stars: ✭ 41 (-68.46%)
Mutual labels:  event-sourcing, event-driven
Vertex
Vertex is a distributed, ultimately consistent, event traceable cross platform framework based on Orleans, which is used to build high-performance, high throughput, low latency, scalable distributed applications
Stars: ✭ 117 (-10%)
Mutual labels:  event-sourcing, event-driven

Requirements as code

Build Status Gitter

requirements as code logo

Requirements as code enables you to translate use cases to code to build maintainable applications.

This page describes simple ways to get started.

You will see how to create actors with use case models that react to individual messages.

For sequences of interactions, create a model with flows instead.

An actor running such a model with flows can serve as an easy to understand alternative to state machines.

A model with flows is useful to build user journeys, sagas and process managers.

See this wiki page for an explanation.

You can find code examples for models with flows here.

Getting started

Requirements as code is available on Maven Central.

The size of the core jar file is less than 100 kBytes. It has no further dependencies.

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.9.2</version>
  </dependency>

If you are using Gradle, include the following in your build.gradle, to use the core:

implementation 'org.requirementsascode:requirementsascodecore:1.9.2'

At least Java 8 is required to use requirements as code, download and install it if necessary.

How to create an actor and send messages to it

Let's look at the general steps first. After that, you'll see a concrete code example.

Step 1: Create an actor with a model

class MyActor extends AbstractActor{
  @Override
  public Model behavior() {
    Model model = Model.builder()
      .user(/* command class */).system(/* command handler*/)
      .user(..).system(...)
      ...
    .build();
    return model;
  }
}

For handling commands, the message handler has a Consumer<T> or Runnable type, where T is the message class. For handling queries, use .systemPublish instead of .system, and the message handler has a Function<T, U> type. For handling events, use .on() instead of .user(). For handling exceptions, use the specific exception's class or Throwable.class as parameter of .on().

Use .condition() before .user()/.on() to define an additional precondition that must be fulfilled. You can also use condition(...) without .user()/.on(), meaning: execute at the beginning of the run, or after an interaction, if the condition is fulfilled. Use .step(...) before .user()/.on() to explicitly name the step - otherwise the steps are named S1, S2, S3...

The order of user(..).system(...) statements has no significance here.

Note that the Actor class is not thread-safe, and it's not an active class that runs in its own thread.

Step 2: Send a message to the actor

MyActor actor = new MyActor();
Optional<T> queryResultOrEvent = actor.reactTo(<Message POJO Object>);

Instead of T, use the type you expect to be published. Note that reactTo() casts to that type, so if you don't know it, use Object for T. If an unchecked exception is thrown in one of the handler methods, reactTo() will rethrow it. The call to reactTo() is synchronous.

Code example

There's an actor with a single use case with a single interaction.

The user sends a request with the user name ("Joe"). The system says hello ("Hello, Joe.")

package helloworld;

import java.util.function.Consumer;

import org.requirementsascode.AbstractActor;
import org.requirementsascode.Model;

public class HelloUser {
  public static void main(String[] args) {
    GreetingService greeter = new GreetingService(HelloUser::saysHello);
    greeter.reactTo(new RequestHello("Joe"));
  }
  
  private static void saysHello(RequestHello requestsHello) {
    System.out.println("Hello, " + requestsHello.getUserName() + ".");
  }
}

class GreetingService extends AbstractActor {
  private static final Class<RequestHello> requestsHello = RequestHello.class;
  private final Consumer<RequestHello> saysHello;

  public GreetingService(Consumer<RequestHello> saysHello) {
    this.saysHello = saysHello;
  }

  @Override
  public Model behavior() {
    Model model = Model.builder()
      .user(requestsHello).system(saysHello)
    .build();
    return model;
  }
}

class RequestHello {
  private final String userName;

  public RequestHello(String userName) {
    this.userName = userName;
  }

  public String getUserName() {
    return userName;
  }
}

Applying the requirements as code design principles

The example above has shown how to create an actor, and send messages to it. In practice, that already gives you the benefit of recording the interaction in the code for long term maintenance. To apply the requirements as code design principles, to clearly separate requirements from realization and get to a pure domain model, the above example needs to be expanded as follows.

Actor

Create a subclass of AbstractActor, and override its behavior() method to provide the model.

Pass the message handlers as constructor parameters.

Use interfaces, not concrete classes, as constructor parameters.

That let's you change the concrete message handler from the outside.

Message senders

There needs to be someone who's sending messages to the actor. In practice, this could be a Spring Controller, or a desktop GUI, for example. Pass the actor to the message sender as a constructor parameter. After that, the sender can send messages to the actor.

class MessageSender {
  private final AbstractActor greetingService;

  public MessageSender(AbstractActor greetingService) {
    this.greetingService = greetingService;
  }

  public void sendMessages() {
    greetingService.reactTo(new RequestHello("Joe"));
  }
}

Messages

Messages should be simple and immutable POJOs. They just carry the information needed to be processed by the message handler. No domain logic is allowed here. In the example, the RequestHello class represents a command that carries the user name.

class RequestHello {
  private final String userName;

  public RequestHello(String userName) {
    this.userName = userName;
  }

  public String getUserName() {
    return userName;
  }
}

Message handlers

Message handlers orchestrate the calls to the infrastructure and domain code. They are 'dumb' in the sense that they don't contain business logic themselves. For testability, pass in all collaborators via constructor parameters.

class SayHello implements Consumer<RequestHello> {
  private final OutputAdapter outputAdapter;

  public SayHello(OutputAdapter outputAdapter) {
    this.outputAdapter = outputAdapter;
  }

  public void accept(RequestHello requestHello) {
    String greeting = Greeting.forUser(requestHello.getUserName());
    outputAdapter.showMessage(greeting);
  }
}

Infrastructure classes

These are classes that connect to external services or the infrastructure. In the example, this is the class that prints the message to the console.

class OutputAdapter{
  public void showMessage(String message) {
    System.out.println(message);
  }
}

Pure domain code

These are the domain classes. They don't communicate with the technical infrastructure, since all communication with the infrastructure happens in the message handler.

In the example, there is only a single domain function: for creating a greeting, based on the user name.

class Greeting{
  public static String forUser(String userName) {
    return "Hello, " + userName + ".";
  }
}

Complete example code for applying the design priciples

Here's the complete example as a single file for convenience.

package actor;

import java.util.function.Consumer;

import org.requirementsascode.AbstractActor;
import org.requirementsascode.Model;

public class ActorExample {
  public static void main(String[] args) {
    final OutputAdapter outputAdapter = new OutputAdapter();
    AbstractActor greetingService = new GreetingService(new SayHello(outputAdapter));
    new MessageSender(greetingService).sendMessages();
  }
}

/**
 * Actor that owns and runs the use case model, and reacts to messages by
 * dispatching them to message handlers.
 */
class GreetingService extends AbstractActor {
  private static final Class<RequestHello> requestsHello = RequestHello.class;
  private final Consumer<RequestHello> saysHello;

  public GreetingService(Consumer<RequestHello> saysHello) {
    this.saysHello = saysHello;
  }

  @Override
  public Model behavior() {
    Model model = Model.builder()
      .user(requestsHello).system(saysHello)
    .build();
    return model;
  }
}

/**
 * Message sender class
 */
class MessageSender {
  private final AbstractActor greetingService;

  public MessageSender(AbstractActor greetingService) {
    this.greetingService = greetingService;
  }

  /**
   * Send message to the service actor. In this example, we don't care about the
   * return value of the call, because we don't send a query or publish events.
   */
  public void sendMessages() {
    greetingService.reactTo(new RequestHello("Joe"));
  }
}

/**
 * Command class
 */
class RequestHello {
  private final String userName;

  public RequestHello(String userName) {
    this.userName = userName;
  }

  public String getUserName() {
    return userName;
  }
}

/**
 * Message handler
 */
class SayHello implements Consumer<RequestHello> {
  private final OutputAdapter outputAdapter;

  public SayHello(OutputAdapter outputAdapter) {
    this.outputAdapter = outputAdapter;
  }

  public void accept(RequestHello requestHello) {
    String greeting = Greeting.forUser(requestHello.getUserName());
    outputAdapter.showMessage(greeting);
  }
}

/**
 * Infrastructure class
 */
class OutputAdapter {
  public void showMessage(String message) {
    System.out.println(message);
  }
}

/**
 * Domain class
 */
class Greeting {
  public static String forUser(String userName) {
    return "Hello, " + userName + ".";
  }
}

Publishing events

When an actor's behavior only uses the system() method, it's restricted to just consuming messages. But an actor can also publish events with systemPublish(), as shown in this file:

class PublishingActor extends AbstractActor {
  @Override
  public Model behavior() {
    Model model = Model.builder()
      .user(EnterName.class).systemPublish(this::publishNameAsString)
      .on(String.class).system(this::displayNameString)
    .build();
    return model;
  }

  private String publishNameAsString(EnterName enterName) {
    return enterName.getUserName();
  }

  public void displayNameString(String nameString) {
    System.out.println("Welcome, " + nameString + ".");
  }
}

As you can see, publishNameAsString() takes a command object as input parameter, and returns an event to be published. In this case, a String.

By default, the actor takes the returned event and publishes it to the same model, as shown above. But you can also publish events to a different actor. That receiving actor will react to the event.

The syntax is:

.user(/* command class */).systemPublish(/* event producing function*/).to(/* receiving actor */)

or

.on(/* event class */).systemPublish(/* event producing function*/).to(/* receiving actor */)

Here is an example of two actors. The MessageProducer receives an EnterName command and sends a NameEntered event to the MessageConsumer. The consumer receives the event, and prints the name.

class MessageProducer extends AbstractActor {
  private AbstractActor messageConsumer;

  public MessageProducer(AbstractActor messageConsumer) {
    this.messageConsumer = messageConsumer;
  }
  
  @Override
  public Model behavior() {
    Model model = Model.builder()
      .user(EnterName.class).systemPublish(this::nameEntered).to(messageConsumer)
    .build();
    return model;
  }

  private NameEntered nameEntered(EnterName enterName) {
    return new NameEntered(enterName.getUserName());
  }
}

class MessageConsumer extends AbstractActor {
  @Override
  public Model behavior() {
    Model model = Model.builder()
      .on(NameEntered.class).system(this::displayName)
    .build();
    return model;
  }

  public void displayName(NameEntered nameEntered) {
    System.out.println("Welcome, " + nameEntered.getUserName() + ".");
  }
}

To access the model runner inside of an actor, call super.getModelRunner().

Note that in any case, an actor returns the event that was published last to the caller of actor.reactTo().

Influences and special features

Requirements as code is influenced by the ideas of clean architecture and hexagonal architecture. It can be used to implement them.

You can use this library to publish DDD Domain Events without littering your code with calls to a domain event publisher. Instead, your command handler returns the event. Your event publisher will pick it up automatically.

The use case model at the boundary represents the single source of truth for interactions started by the user. That's why you can generate living documentation from the use case model. The generated use case documents represent always up to date information about how the system works from a user's perspective.

Further documentation of requirements as code

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".
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].