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.
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
_beforefield contains the previous state of the aggregate, while_aftercontains 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 Summary
ConstructorsConstructorDescriptionThis will install a standard reducer for the commandsdelete,patchandput. -
Method Summary
Modifier and TypeMethodDescriptionapp()Returns the app name.net.pincette.rs.streams.Streams<String,JsonObject, T, U> build()This builds theStreamstopology for the aggregate.static CompletionStage<JsonObject>delete(JsonObject currentState) The standard delete reducer.Returns the environment.fullType()Returns the full aggregate type, which is composed as <app>-<type>.static CompletionStage<JsonObject>patch(JsonObject command, JsonObject currentState) The standard patch reducer.static CompletionStage<JsonObject>put(JsonObject command) The standard put reducer.static Reducerreducer(BinaryOperator<JsonObject> transformer) Wraps a generic transformer in aReducer.static Reducerreducer(UnaryOperator<JsonObject>... transformers) Wraps a sequence of generic transformers in aReducer.Returns the topic name in the form <application>-<type>-purpose-< environment>.type()Returns the aggregate type.Sets the name of the application.withBackpressureTimeout(Duration backpressureTimeout) Honors the JWT fieldbreakingTheGlasswhen checking ACLs.withBuilder(net.pincette.rs.streams.Streams<String, JsonObject, T, U> builder) Sets theStreamsbuilder that will be used to create the topology.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.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.withEnvironment(String environment) Sets the environment in which this aggregate will live.withLogger(Logger logger) Sets the logger, which will be used when the log level isFINEST.withMongoClient(com.mongodb.reactivestreams.client.MongoClient client) Sets the MongoDB client that is needed to create a session.withMongoDatabase(com.mongodb.reactivestreams.client.MongoDatabase database) The MongoDB database in which the events and aggregates are written.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.withReducer(String command, Reducer reducer) Sets the reducer for the given command.withReducer(Reducer reducer) Sets the reducer for all commands, which means the reducer does the dispatching itself.Sets the aggregate type, which will become the suffix for the full aggregate type.withUniqueExpression(JsonValue expression) When the expression is given it is used on commands to obtain an alternate unique key.
-
Constructor Details
-
Aggregate
public Aggregate()This will install a standard reducer for the commandsdelete,patchandput.- Since:
- 1.0
-
-
Method Details
-
delete
The standard delete reducer. It sets the field_deletedtotrue.- Parameters:
currentState- the current state of the aggregate.- Returns:
- The new state of the aggregate.
- Since:
- 1.0
-
patch
The standard patch reducer. It expects to find the_opsfield, 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
The standard put reducer. It just removes the_commandfield 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
Wraps a generic transformer in aReducer. 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
Wraps a sequence of generic transformers in aReducer. The result of one transformer is fed to the next. The JSON object that is given to the sequence has the fieldscommandandstate. The transformer should produce the new state.- Parameters:
transformers- the given transformer sequence.- Returns:
- The wrapped transformer sequence.
- Since:
- 1.1.4
-
app
Returns the app name.- Returns:
- The app name.
- Since:
- 1.0
-
build
This builds theStreamstopology for the aggregate.- Returns:
- The builder that was given before.
- Since:
- 1.0
-
environment
Returns the environment.- Returns:
- The environment.
- Since:
- 1.0
-
fullType
Returns the full aggregate type, which is composed as <app>-<type>.- Returns:
- The full aggregate type.
- Since:
- 1.1
-
topic
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
Returns the aggregate type.- Returns:
- The aggregate type.
- Since:
- 1.0
-
withApp
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
-
withBreakingTheGlass
Honors the JWT fieldbreakingTheGlasswhen checking ACLs. This should always be used together with auditing.- Returns:
- The aggregate object itself.
- Since:
- 1.0
-
withBuilder
Sets theStreamsbuilder 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
Sets the environment in which this aggregate will live. The default isdev. 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
Sets the logger, which will be used when the log level isFINEST.- Parameters:
logger- the logger.- Returns:
- The aggregate object itself.
- Since:
- 1.3
-
withMongoClient
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
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
Sets the reducer for the given command.- Parameters:
command- the name of the command, which will match the_commandfield.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 thecommandandaggregatefields. 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_commandfield.reducer- the reducer processor.- Returns:
- The aggregate object itself.
- Since:
- 3.1
-
withReducer
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
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
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
-