Class Aggregate<T,U>

java.lang.Object
net.pincette.jes.Aggregate<T,U>
Type Parameters:
T - the value type for the topic source.
U - the value type for the topic sink.

public class Aggregate<T,U> extends Object
This class manages the state of JSON aggregates. You give it reducers, which calculate the new aggregate state with the current one and an incoming command. For every aggregate instance all commands are processed sequentially. The result of a reducer execution is compared with the current aggregate state and the difference is emitted as an event. A reducer may also find problems in the command. In that case it should return the command, marked with "_error": true. No event will be emitted then.

The external interface at runtime is a set of topics. Their names always have the form <app>-<type>-<purpose>[-<environment>]. The following topics are expected to exist (the names are the purpose):

aggregate
On this topic the current state of the aggregate is emitted.
command
Through this topic commands are received. It is the only input of the system.
event
On this topic the events are emitted, which contain the changes between two subsequent aggregate versions.
event-full
The events are also emitted on this topic, but here they have two extra fields. The _before field contains the previous state of the aggregate, while _after contains the current one. This is for consumers that want to do other kinds of analysis than the plain difference.
reply
On this topic either the new aggregate or the failed command it emitted. The topic is meant to be routed back to the end-user, for example through Server-Sent Events. A reactive client can pick it up and update its stores. This connects the server and the client in one reactive loop.

An aggregate is a JSON document, which has the following technical fields on top of whatever you put in it:

_corr
The correlation identifier that was used by the last command. It is usually a UUID.
_deleted
This boolean marks the aggregate instance as deleted. This is a logical delete.
_id
The identifier of the aggregate instance. It is usually a UUID.
_jwt
The decoded JSON Web Token that was used by the last command.
_seq
A sequence number. This is the sequence number of the last event.
_type
The aggregate type, which is composed as <application>-<name>.

A command is a JSON document, which has the following technical fields on top of whatever you put in it:

_command
The name of the command. This field is mandatory.
_corr
A correlation identifier. It is propagated throughout the flow. This is usually a UUID.
_error
This boolean indicates there is a problem with the command.
_id
The identifier of the aggregate instance. It is usually a UUID. This field is mandatory.
_jwt
The decoded JSON Web Token.
_languages
An array of language tags in the order of preference. When a validator or some other component wishes to send messages to the user, it can use the proper language for it.
_seq
A sequence number. If this field is present it should have the same value as that field in the aggregate instance. Otherwise the command is ignored.
_type
The aggregate type, which is composed as <application>-<name>. This field is mandatory.

An event is a JSON document, which has the following technical fields:

_after
An optional field that carries the new state of the aggregate instance.
_before
An optional field that carries the previous state of the aggregate instance.
_command
The name of the command that caused the event to be created.
_corr
The correlation identifier that was used by the last command. It is usually a UUID.
_id
The identifier of the aggregate instance. It is usually a UUID.
_jwt
The decoded JSON Web Token that was used by the last command.
_ops
An array of operations as described in RFC 6902. It describes how an aggregate instance has changed after the reduction of a command.
_seq
A sequence number. There should not be holes in the sequence. This would indicate corruption of the event log.
_timestamp
The timestamp in epoch millis.
_type
The aggregate type, which is composed as <application>-<name>.
Since:
1.0
Author:
Werner Donné
  • Constructor Details

    • Aggregate

      public Aggregate()
      This will install a standard reducer for the commands delete, patch and put.
      Since:
      1.0
  • Method Details

    • delete

      public static CompletionStage<JsonObject> delete(JsonObject currentState)
      The standard delete reducer. It sets the field _deleted to true.
      Parameters:
      currentState - the current state of the aggregate.
      Returns:
      The new state of the aggregate.
      Since:
      1.0
    • patch

      public static CompletionStage<JsonObject> patch(JsonObject command, JsonObject currentState)
      The standard patch reducer. It expects to find the _ops field, which contains a JSON patch and applies it to the aggregate.
      Parameters:
      currentState - the current state of the aggregate.
      command - the given command.
      Returns:
      The new state of the aggregate.
      Since:
      1.0
    • put

      public static CompletionStage<JsonObject> put(JsonObject command)
      The standard put reducer. It just removes the _command field and uses everything else as the new state of the aggregate.
      Parameters:
      command - the given command.
      Returns:
      The new state of the aggregate.
      Since:
      1.0
    • reducer

      public static Reducer reducer(BinaryOperator<JsonObject> transformer)
      Wraps a generic transformer in a Reducer. The first argument will be the command and the second the current state of the aggregate.
      Parameters:
      transformer - the given transformer.
      Returns:
      The wrapped transformer.
      Since:
      1.1.4
    • reducer

      @SafeVarargs public static Reducer reducer(UnaryOperator<JsonObject>... transformers)
      Wraps a sequence of generic transformers in a Reducer. The result of one transformer is fed to the next. The JSON object that is given to the sequence has the fields command and state. The transformer should produce the new state.
      Parameters:
      transformers - the given transformer sequence.
      Returns:
      The wrapped transformer sequence.
      Since:
      1.1.4
    • app

      public String app()
      Returns the app name.
      Returns:
      The app name.
      Since:
      1.0
    • build

      public net.pincette.rs.streams.Streams<String,JsonObject,T,U> build()
      This builds the Streams topology for the aggregate.
      Returns:
      The builder that was given before.
      Since:
      1.0
    • environment

      public String environment()
      Returns the environment.
      Returns:
      The environment.
      Since:
      1.0
    • fullType

      public String fullType()
      Returns the full aggregate type, which is composed as <app>-<type>.
      Returns:
      The full aggregate type.
      Since:
      1.1
    • topic

      public String topic(String purpose)
      Returns the topic name in the form <application>-<type>-purpose-< environment>.
      Parameters:
      purpose - one of "aggregate", "command", "event", "event-full" or "reply".
      Returns:
      The topic name.
    • type

      public String type()
      Returns the aggregate type.
      Returns:
      The aggregate type.
      Since:
      1.0
    • withApp

      public Aggregate<T,U> withApp(String app)
      Sets the name of the application. This will become the prefix of the aggregate type.
      Parameters:
      app - the application name.
      Returns:
      The aggregate object itself.
      Since:
      1.0
    • withBackpressureTimeout

      public Aggregate<T,U> withBackpressureTimeout(Duration backpressureTimeout)
    • withBreakingTheGlass

      public Aggregate<T,U> withBreakingTheGlass()
      Honors the JWT field breakingTheGlass when checking ACLs. This should always be used together with auditing.
      Returns:
      The aggregate object itself.
      Since:
      1.0
    • withBuilder

      public Aggregate<T,U> withBuilder(net.pincette.rs.streams.Streams<String,JsonObject,T,U> builder)
      Sets the Streams builder that will be used to create the topology.
      Parameters:
      builder - the given builder.
      Returns:
      The aggregate object itself.
      Since:
      3.0
    • withCommandProcessor

      public Aggregate<T,U> withCommandProcessor(Flow.Processor<net.pincette.rs.streams.Message<String,JsonObject>,net.pincette.rs.streams.Message<String,JsonObject>> commandProcessor)
      Inserts a processor between the command topic and the reducers.
      Parameters:
      commandProcessor - the processor.
      Returns:
      The aggregate object itself.
      Since:
      1.0
    • withCommandProcessor

      public Aggregate<T,U> withCommandProcessor(String command, Flow.Processor<net.pincette.rs.streams.Message<String,JsonObject>,net.pincette.rs.streams.Message<String,JsonObject>> commandProcessor)
      Inserts a processor between the command topic and a specific reducer. If there also is a general preprocessor, then it will come before it.
      Parameters:
      command - the name of the command.
      commandProcessor - the processor.
      Returns:
      The aggregate object itself.
      Since:
      3.1
    • withEnvironment

      public Aggregate<T,U> withEnvironment(String environment)
      Sets the environment in which this aggregate will live. The default is dev. It will become the suffix for all the topic names. Typically the value for this comes from an external configuration.
      Parameters:
      environment - the name of the environment.
      Returns:
      The aggregate object itself.
      Since:
      1.0
    • withLogger

      public Aggregate<T,U> withLogger(Logger logger)
      Sets the logger, which will be used when the log level is FINEST.
      Parameters:
      logger - the logger.
      Returns:
      The aggregate object itself.
      Since:
      1.3
    • withMongoClient

      public Aggregate<T,U> withMongoClient(com.mongodb.reactivestreams.client.MongoClient client)
      Sets the MongoDB client that is needed to create a session.
      Parameters:
      client - the given client.
      Returns:
      The aggregate object itself.
      Since:
      4.0
    • withMongoDatabase

      public Aggregate<T,U> withMongoDatabase(com.mongodb.reactivestreams.client.MongoDatabase database)
      The MongoDB database in which the events and aggregates are written. The database will be used with majority read and write concerns.
      Parameters:
      database - the database connection.
      Returns:
      The aggregate object itself.
      Since:
      1.0
    • withReducer

      public Aggregate<T,U> withReducer(String command, Reducer reducer)
      Sets the reducer for the given command.
      Parameters:
      command - the name of the command, which will match the _command field.
      reducer - the reducer function.
      Returns:
      The aggregate object itself.
      Since:
      1.0
    • withReducer

      public Aggregate<T,U> withReducer(String command, Flow.Processor<net.pincette.rs.streams.Message<String,JsonObject>,net.pincette.rs.streams.Message<String,JsonObject>> reducer)
      Sets the reducer for the given command. The processor will receive messages with the command and aggregate fields. It should produce the new aggregate. The ID of the messages is the aggregate ID.
      Parameters:
      command - the name of the command, which will match the _command field.
      reducer - the reducer processor.
      Returns:
      The aggregate object itself.
      Since:
      3.1
    • withReducer

      public Aggregate<T,U> withReducer(Reducer reducer)
      Sets the reducer for all commands, which means the reducer does the dispatching itself. Note that the individual reducers are not tried when this is set.
      Parameters:
      reducer - the reducer function.
      Returns:
      The aggregate object itself.
      Since:
      1.0
    • withType

      public Aggregate<T,U> withType(String type)
      Sets the aggregate type, which will become the suffix for the full aggregate type.
      Parameters:
      type - the type.
      Returns:
      The aggregate object itself.
      Since:
      1.0
    • withUniqueExpression

      public Aggregate<T,U> withUniqueExpression(JsonValue expression)
      When the expression is given it is used on commands to obtain an alternate unique key. This can be used to avoid the creation of duplicates according to some business criterion. The "unique" topic must exist in this case.
      Parameters:
      expression - a MongoDB expression.
      Returns:
      The aggregate object itself.
      Since:
      1.2