BernhardBln / spring-cqs

Simple abstractions and aspects to communicate within one process following the CQS principle

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Spring-CQS

Simple abstractions we use to follow the CQS Principle in applications.

Java CI Codacy Badge codecov MavenCentral

Motivation

The CQS Principle states that "every method should either be a command that performs an action, or a query that returns data to the caller, but not both." in order to reduce side-effects.

In our projects we use abstractions like Query & QueryHandler as well as Command & CommandHandler to follow this principle. However, there is a bit of fineprint here that makes it worthwhile to reuse this in form of a library:

  • a command / a query needs to be valid (as in java.validation valid), otherwise a Command/Query-ValidationExcption will be thrown
  • a command / a query needs to be valid (determined by an optional message on the handler), otherwise a Command/Query-ValidationExcption will be thrown
  • a command / a query needs to be verified by a mandatory method in the handler the is expected to throw a Command/Query-VerificationException
  • when a command / a query is handled, any exception it may throw is to be wrapped in a Command/Query-Handling Exception

In order to accomplish that, this kind of orchestration is done by an aspect, in order to get this out of that way when following the call stack in your IDE.

This has pros and cons and might be a debatable use of aspects, but we decided that this is the best solution for our context. You know, it depends...

Usage

This is meant to be used with Spring Boot. In order to get this running just add the dependency to your build system:

Maven

    <dependency>
      <groupId>eu.prismacapacity</groupId>
      <artifactId>spring-cqs</artifactId>
      <version><!-- put the desired version in here--></version>
    </dependency>

Configuration

The only thing you might want to configure is how Cqs uses Metrics. See @CqsConfiguration for details.

Example

Let's say, you have a Foo Entity and a corresponding repository. What we do with this lib is to encapsulate use-cases in a UI-agnosic manner.

class FooEntity {
}

class FooQuery implements Query {
    @NotNull
    UUID idToLookFor;
    
    @NotNull
    Long userIdOfRequestingUser;
}

class FooHandler implements QueryHandler<FooQuery, List<FooEntity>> {

    @Override
    public void verify(@NonNull FooQuery query) throws QueryVerificationException {
        // check if the preconditions for the query to be executed are met.
        // we know userIdOfRequestingUser is not null (otherwise it would not have passed validation)
        // but maybe we need to check if the user is assigned to the right organisation or something...
    }

    @Override
    public List<FooEntity> handle(@NonNull FooQuery query) throws QueryHandlingException, QueryTimeoutException {
        return myFooRepository.findById(query.idToLookFor);
    }
}

The idea here is (beyond javax.validation), you can quickly see the ins and outs of a use-case, may it be Query or Command, including checking for instance security constraints in a programmatic and technology agnostic way. Also this creates a nice seam between UI/Rest Layer and Domain Model or persistence model in case this is the same for you. If you're interested in checking and maintaing those bounds, have a look at for instance Archunit.

About

Simple abstractions and aspects to communicate within one process following the CQS principle


Languages

Language:Java 99.1%Language:Shell 0.9%