Giter Site home page Giter Site logo

bertilmuth / requirementsascode Goto Github PK

View Code? Open in Web Editor NEW
166.0 10.0 21.0 2.75 MB

Behavior driven service development.

License: Apache License 2.0

Java 99.86% FreeMarker 0.14%
living-documentation event-driven message-driven java behavior-driven-development behavior

requirementsascode's People

Contributors

bertilmuth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

requirementsascode's Issues

ModelRunner.restart() should run the model

Currently, calling ModelRunner.restart() only resets internal variables to their initial state.

When calling ModelRunner.stop() first, and then restart(), the ModelRunner is not running.
That is confusing.

Instead, restart should really rerun the model.

BREAKING CHANGE: return published event from ModelRunner.reactTo()

Currently, ModelRunner.reactTo() returns the latest step that has been run. This information is the same as calling ModelRunner.getLatestStep(), so it's redundant and only there for historical reasons.

It would be much more helpful if ModelRunner.reactTo() returned the latest event that has been published as a consquence of the call. To cover the case that none was published, it should return Optional<Object>.

Allow to cause system reaction based on condition only

Currently, it is not possible to leave out the event class in the simple event handling case,
and react as soon as a condition is fulfilled (in contrast to the use case models, where this is already possible).

So the request is to implement reacting in the .when only case.

Join the community

Are you using requirementsascode beyond the examples? If not, what's keeping you from it?
If there is one thing you would improve, what would that be?

For feedback, or a chat with other users and myself, join the Gitter community:
Requirements as Code on Gitter

Reintroduce ModelRunner as optional choice in autonomous system reactions

In most situations, specifying a system reaction as Runnable or reference to a method without arguments is the way to go. This was the breaking change introduced in 0.9.0.

However, examples have shown that in some circumstances, having a model runner as a system reaction's argument can be helpful.

So this should be available optionally.

New method to query event types of events the runner can react to

The ModelRunner has a method canReactTo(eventClass) that checks whether the model runner can currently react to the events of the specified eventClass.

What is missing is the possibility to query all the event classes that the runner can currently react to.

The solution should introduce a new method getReactToTypes that returns those event classes.

Get rid of UnhandledException

At the moment, when a system reaction method throws an exception that is not
handled somewhere in the model, the ModelRunner throws an UnhandledException, wrapping the thrown exception.

That can be confusing for users.,
So the solution is: unhandled exceptions will be rethrown without modification.

StackOverflowException thrown instead of MoreThanOneStepCanReact exception

Bug description:
Actual: When there are 2 steps that can react, in two flows that have NO condition specified, and the steps react to the same event, the UseCaseModelRunner throws a StackOverflowException.
Expected: the UseCaseModelRunner throws a MoreThanOneStepCanReact exception.

Steps to reproduce:
Create/run a use case model similar to the following:

     UseCaseModel useCaseModel = useCaseModelBuilder
       .useCase("Use Case")
         .basicFlow()
           .step("Step 1").user(String.class).system(s -> System.out.println(s))
       .useCase("Another Use Case")
         .basicFlow()
           .step("Step 2 with same event as Step 1").user(String.class).system(s -> System.out.println(s))
       .build();
     
    useCaseModelRunner.run(useCaseModel);
    useCaseModelRunner.reactTo(new String("Some text"));

Requirementsascode version: 0.4.0
Operation system: Windows 10/Eclipse, but should occur in any environment

Update hexagon architecture example

The hexagon example is a bit outdated. The idea is to update it to new insights, to turn it into a lightweight example of an architecture that has a application / use case layer.

Including use cases

Right now, requirementsascode does not allow to include other use cases from a use case.
Including use cases makes it possible to reuse steps from other use cases.
It avoids error prone duplication of steps between use cases.

R1. The solution shall allow to include whole use cases, with all of their flows. For this, a new .includeUseCase statement shall be created.
R2. Including shall be possible from any place in a flow where a step definition is possible.
R3a. When the runner reaches an .includeUseCase statement, the runner shall continue with the first step of the basic flow of the included flow, with the exception that an alternative flow of the included use case may interrupt.
R3b. When the last step of an included flow has been run, the runner shall continue with the next step of the including flow (after the .includeUseCase statement).

ModelRunner.canReactTo should return true for event subclass

According to the Javadoc comment for ModelRunner.canReactTo(), the method must return true if its argument matches an event class in the model, or a sub class of it.

Right now, it only works if the exact event class matches. Not for subclasses.
The current behavior is inconsistent with ModelRunner.reactTo(), and needs to change to match the Javadoc comment.

ModelRunner.reactTo() should accept list of events

Currently, the ModelRunner.reactTo() method either accepts a single event, or mutliple events as varargs (comma-separated).

When, by accident, the user of requirementsascode uses a list as parameter, this will be treated as a single event, rather than a list of events.

So the request is: change this behavior, treat a list passed to ModelRunner.reactTo() as a list of events.

ModelBuilder should be more strict

The ModelBuilder allows certain configurations that are superfluous.

For example, it allows to specify more than one when conditions, but starting with the second one, they get overwritten.

The solution should eliminate these cases.

Default actor (per use case)

Right now, if you want to specify an actor for a use case, you need to specify it for each step.
Like so:

Model model = 
	modelBuilder.useCase(USE_CASE).basicFlow()
		.step(CUSTOMER_ENTERS_TEXT).as(customer).user(EntersText.class).system(displaysEnteredText())
		.step(CUSTOMER_ENTERS_TEXT_AGAIN).as(customer).user(EntersText.class).system(displaysEnteredText())
	.build();

It would be nice to be able to specify a default actor for a whole use case. It will be used for every step that includes a .user() part. Like so:

Model model = 
	modelBuilder.useCase(USE_CASE).as(customer).basicFlow()
		.step(CUSTOMER_ENTERS_TEXT).user(EntersText.class).system(displaysEnteredText())
		.step(CUSTOMER_ENTERS_TEXT_AGAIN).user(EntersText.class).system(displaysEnteredText())
	.build();

As you can see, the default actor needs to be specified after the .useCase() part, right before the .basicFlow().

Enable event publishing in the model

Right now, a system reaction method specified by .system can only consume events.
While publishing events is possible through injecting a dependency (to an event publisher) into the system reaction method, there is no support for explicitly modeling event publishing yet.

The solution requested is: create a new method .submitPublish in the model builder that doesn't only consume events, but can also return events.

The model runner takes the returned events and sends them to its own .reactTo method. This default behavior can be overriden by specifying a custom event handler with .publishWith.

Breaking API change: rename adaptSystemReaction and SystemReactionTrigger

Because of issue #13, handling of "unhandled" events will be supported.
So there will be the need to adapt the system reaction for 2 scenarios:
for the "handled" events case, and for the "unhandled" event case.

Goal:
Rename modelRunner's adaptSystemReaction to handleWith,
and introduce the new method as handleUnhandledWith .
Rename SystemReactionTrigger to StandardEventHandler.

Breaking change: integrate TestModelRunner methods in ModelRunner

Recording steps in a TestModelRunner is nice, but what if you need to record steps in production? It would be more clear of you could use the same ModelRunner for everything.

So the methods of the TestModelRunner should be integrated to the standard ModelRunner .
Additionally, methods startRecording() and stopRecording() should be added to allow for fine grained control of when to record.

Handling of unhandled events

Up to v0.7.2, events that the runner does not react to are silently consumed.

Goal: implement a handler for "unhandled" events.

New method to check steps that have been run in TestModelRunner

Currently, the getRunStepNames method of the TestModelRunner returns a string of the step names, joined by semicolons.

Asserting this string is error prone (but good for detailed comparison).

So there will be another method, hasRun, returning a boolean, with string array input, to check whether specified steps have been run.

Breaking change: rename step.when to step.condition

The when method to specify a precondition of a step is not properly named.

The name caused users to think that the step is only executed at a distinct point in time (like an event).
But the truth is: the step is executed while the condition holds.

To clarify that, when should be renamed to condition.

Note that the API should stay stable in the v1.x versions after that change.

Incrementally build flows

Up to v0.6.0, it was only possible to build flows in one go, using the UseCaseModelBuilder.

Starting with v0.6.1, it will be possible to build flows incrementally.

So the following two examples are equivalent:

Example 1:

useCaseModelBuilder.useCase("Use Case 1").basicFlow()
	.step("1").system(reactsToStep1())
	.step("2").system(reactsToStep2());

Example 2:

useCaseModelBuilder.useCase("Use Case 1").basicFlow()
        .step("1").system(reactsToStep1());

useCaseModelBuilder.useCase("Use Case 1").basicFlow()
	.step("2").system(reactsToStep2());

ModelRunner performance boost (by reducing garbage collection)

Up until requirements as code v1.0.0, the focus was on getting a stable API definition, not performance.

The ModelRunner class creates several collections for each call to reactTo(), leading to a lot of garbage collection. That's why in my personal tests, the throughput was limited to around 800k events per second.

By reducing the amount of garbage collection, the goal is to improve throughput to at least a few million events per second.

Downloaders of "old versions" (<= 0.9.3): Please give feedback

Dear downloaders of requirements as code.

I can see in the statistics every month that there are still downloads of the "old versions" of requirements as code. While, of course, you have every right to do so, I am curious why you don't use the later versions.

Please let me know in the comments, or write me an email to: [email protected]

Thank you.

Bertil

Ensure Java 12 compatibility

Since Java 12 has reached GA, the compatibility must be checked.
This is done by adapting the Travis CI configuration.

In example HelloWorld04, age needs to be entered before second prompt

Accidentally, in example HelloWorld04, the user needs to enter her age before being prompted to do so.

The problem is caused by this line:
modelRunner.reactTo(entersText(), entersText());

The reason for that is that both entersText() method calls are evaluated before reactTo() is called,
and entersText() is where user input happens, so both inputs are done at once.

This can be fixed easily by making to disting reactTo() calls.

Enable .user definition for flowless steps

So far, steps outside of a flow can only contain an .on(..) statement for defining the types of events to process.

So the proposal is: enable explicitly defining user commands with a .user(...) statement, analogous to the steps of a flow.

Breaking change: new API for StandardEventHandler (StepToBeRun)

The StandardEventHandler class name is misleading - an instance of it contains information about the step to be run. So it should be renamed to StepToBeRun, and include a run() method that triggers the system reaction.

So far, it is also cumbersome to find out details about the step to be run, in a custom event handler method (as defined with modelRunner.handleWith()).

In the future, the StepToBeRun class should instead have the following methods:

  • getStepName() that returns a String (the name of the step to be executed).
  • getCondition() that returns an Optional<? extends Condition> (the precondition of the step to be executed, or Optional.empty() if the user hasn't defined a precondition).
  • getEvent() that returns an Optional<? extends Object> (the event of the step to be executed, or Optional.empty() if the user hasn't defined a event).
  • getSystemReaction() that returns an Object (the object representing the system reaction of the step to be executed.

Get rid of included use cases - replace by chained models

When publishing events is enabled (see #43), there is no need anymore for the "include use case" feature.

Rather, chaining models where the higher level model publishes events to the lower level model will be the preferred way. How this is done is documented in the Included Use Case test case.

All code relating to the "include use case" feature will be discarded. This is not considered a breaking change, since the "include use case" feature has previously been undocumented. Please leave a comment if this will cause problems in your implementation.

Provide additional syntax for simple use cases

For simple use cases, e.g. simple CRUD use cases, the syntax up to v0.6.x is not very concise.

The solution will provide addititional syntax that doesn't use flows or steps,
but just handling of events without sequence.

So for a simple hello world example, the code might read:

Model.builder()
    .handles(UserNameEntered.class).with(displayHelloUser())
.build();

A use case may be specified, but if it is not, a default use case ("Handle events") is assumed.

On top of that, the solution should support when conditions.

Make new ModelBuilder().run(..) fluent

Currently, the run method returns void. That makes it impossible to chain with the reactTo method.

So the request is to make the run method return ModelBuilder.

Allow adapting the behavior of TestModelRunner (handleWith method)

Currently, it is only possible to overwrite the default event handling using the modelRunner.handleWith() method.

It is currently not possible to adapt the behavior of the TestModelRunner WITHOUT destroying its step tracking behavior (because that's implemented using modelRunner.handleWith() as well).

A fix is needed, to enable adapting the TestModelRunner's event handling behavior while retaining the step tracking behavior.

Breaking change: get rid of ModelRunner in conditions and system reactions

While it is consistent with the internal implementation, having to specify the ModelRunner as part of conditions, reactWhile and autonomous system reaction methods is confusing to users.

So the future conditions will be specified by users by a functional interface similar to Callable<Boolean>, while the future autonomous system reaction methods will be specified as Runnable.

Extend systemPublish to "condition only" case

Right now, systemPublish() is not available if only a condition, but no event is specified, and you're outside of a flow.

So the proposal is: implement systemPublish() in the above case.

New InfiniteRepetition exception when condition is always true

Currently, when a step has a condition that always evaluates to true, the step is reentered infinitely. This leads to a StackOverflowError.

Instead, a more telling exception (InfiniteRepetition) should be thrown, indicating the step where the infinite repetition occured.

About your Hexagonal Architecture prototype & questions

Hi Bertil,

Here are my remarks about your prototype (https://github.com/bertilmuth/requirementsascode/tree/master/requirementsascodeexamples/hexagon/src/main/java/hexagon).

First, your left-side Adapter (i.e. ConsoleAdapter) must not reference the other right-side Adapters (i.e. WriterAdapter and RepositoryAdapter). The Hexagonal Architecture pattern describe a situation where:

  • All the business-logic must only be located within the hexagon
  • The right-side adapters must only be referenced/aggregated by the hexagon
  • The hexagon must only be referenced/aggregated by the left-side adapters
  • The application (e.g.: your controllers) knows and uses only the left-side adapters to interact with the business-logic embedded within the hexagon

Regarding your state management. You seem to delegate the business-logic state to your use case{Model|ModelBuilder|...} framework. I didn't have time to study the whole thing (with Steps, Flows, Actors, etc) but I'm wondering how you will handle the per-user state-machine.

Anyway. For me the situation here with that UC framework goes a little way out our the Hexagonal Architecture pattern and I would suggest to let your use case "controller" out of the hexagon and making it only deal with the left-side Adapters you have.


Now, to answer your initial question: where to put state dependent behavior? Say user gets happy poem first time she asks. 2nd time sad one. 3rd time another one , I would suggest something like that:

  1. The end-user send a "GiveMeSomePoetry" DTO/Command/Request through a left-side Adapter. Depending on the used technology (http stack, *MQ, etc), the adapter is aware of and capture the identity of the user in order to provide it to the IRequestVerses.GiveMeSomePoetry(...) left-side port verb that must now accept one argument: the identity of the end-user (GiveMeSomePoetry(string userId) for instance)
  2. The PoetryReader hexagon (which implement this left-side port) will then ask a UserSessions instance (a new Domain concept): "what was the last sentiment associated to the poem we had previously delivered to that end-user?". The answer to that question will help the PoetryReader business logic to decide what kind of poem it should ask to the Repository implementing the IObtainPoem right-side port. Of course, this one must be updated in order to provide 3 methods instead of one: getSadPoem(); getHappyPoem(); and getFunnyPoem();

Of course this is only one way to implement it (among many), you can also delegate the UsersSessions/state management to an external system for instance (to make it persistant even after a crash of your service). In that case, you will have to add a new right-side port/adapter for your hexagon business logic to be able to use it.

Hope it will clarify what we already discussed on twitter. Happy coding !

Thomas

Breaking change: rename .handles to .on and .with to .system

Currently, the syntax for simple event processing is:

when(condition).handles(eventClass).with(systemReaction)

The problem with this naming convention will start to occur once it will be possible
to leave out .handles and only define a .when condition:
when(condition).with(systemReaction)

That isn't understandable. Apart from that, it is also inconsistent with how the
use case models are defined.

So the proposal is as described in the issue name. The new form would look like:
when(condition).on(eventClass).system(systemReaction)
or
when(condition).system(systemReaction)
or
on(eventClass).system(systemReaction)

Also, in the use case model, .handles will be renamed to .on as well.

Breaking change: new TestModelRunner methods

Experiments with the current methods of TestModelRunner have shown they are not really serving their purpose well. The getRunStepNames method returns a colon separated list that is too easy to get wrong in the test cases. The hasRun method is good concerning its check, but only returns a boolean value. That value says nothing about the steps that have actually been run.

So replace the old method with two new methods:

getRecordedStepNames
getRecordedEvents

Those methods can be used for easy comparison of the expected vs. actual step names/events that caused a system reaction (they provide the actual, of course).

They both return an array, for easy comparison with JUnits assertArrayEquals method.

Breaking change: change verbs in API to third person singular

The resolution of this issue will change the verbs in the API to third person singular.
So far, it was in the imperative form.
So instead of continueAfter, the new verb will read continuesAfter.

This will provide a more direct translation from use cases to text,
make the models more readable and simplify internal processing.

Fix Akka example

Due to the recent developments, the serialization of the ModelRunner doesn't work any more. That affects the Akka example, that needs to be adapted.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.