Skip to content

Releases: SpineEventEngine/core-java

1.0.0-pre1

03 Nov 14:14
abb3b1c
Compare
Choose a tag to compare
1.0.0-pre1 Pre-release
Pre-release

This is a preview release of the Spine 1.0 version.

New Maven Repository

In this version one more artifact repository is introduced:

The previously used repository is also maintained.

Features and Improvements

Since 0.10.0 several significant features were introduced, making the framework near the feature completeness:

  • Provide automatic idempotency for the business entities.
  • Allow to subscribe to the entity state updates.
  • Allow to produce a command in response to an event or a command — the @Commanding methods.
  • Introduce System bounded context, which tracks the history of each Command, Event and Entity, allowing to trace paths and consequences of incoming end-user imperatives.
  • Make Rejections a special kind of Events to simplify thinking for framework users.

For more details on the features please see #733, #804 and #810 and consult the source code.

In addition the framework now uses Error Prone with a conjunction with the Checker Framework, which allows to catch the potential issues on a compilation stage.

Lots of other less significant improvements were introduced, addressing the issues reported from the real-world use-cases.

Dependency Updates

The framework dependencies were updated as follows.

  • gRPC 1.15.0
  • Protobuf 3.6.1
  • Protobuf Gradle plugin 0.8.5
  • Guava 26.0-jre
  • JUnit 5.3.1
  • Google Truth 0.42

0.10.0

17 Oct 12:42
Compare
Choose a tag to compare
0.10.0 Pre-release
Pre-release

This release brings even more features to the framework, making it very close to 1.0 release.

IMPORTANT. The framework package names were renamed from org.spine3.* to io.spine.*.

Here is a quick summary of changes. For more details please refer to the list of pull requests and Javadocs.

Reacting upon Events and Rejections

It is now possible to emit events in reaction to other events. This is particularly useful for AggregateParts:

/**
 * A part of {@code Task}  aggregate. Defines the essence of a task.
 */
public class TaskDefinition 
    extends AggregatePart<TaskId, Task, TaskVBuilder, TaskRoot> {

    @Assign
    public TaskCompleted handle(CompleteTask cmd) {
        // Handle the command and emit a `TaskCompleted` event.
        // ...
        return taskCompletedEvent;
    }

    @Apply
    void on(TaskCompleted event) {
        // Update state accordingly.
        // ...
    }
} 

/**
 * Another part of {@code Task}  aggregate. Responsible for all comments 
 * of a single task.
 */
public class TaskComments 
    extends AggregatePart<TaskId, TaskComments, TaskCommentsVBuilder, TaskRoot> {

    @React
    public TaskCommentingDisabled handle(TaskCompleted event) {
        // Disable commenting of the completed task.
        //...
        return commentingDisabledEvent;
    }

    @Apply
    void on(TaskCommentingDisabled event) {
        // Update own state as well.
        // ...
    }
}

In the example above TaskComments reacts to the event, emitted by another part and updates own state. Before such a feature became available, an intermediate ProcessManager should have been used for this purpose.

It is also possible to @React upon a rejection in the same manner.

This feature is available for Aggregate, AggregatePart and ProcessManager entities.

Entity API Updates

All the Entity implementations, which are able to handle events, are now descendants of a newly introduced EventPlayingEntity. Its API allows to play events in a unified fashion.

Entity state modification is now available via ValidatingBuilder.

The direct state update, which was previously available to Projections, Aggregates and ProcessManagers via numerous methods (such as updateState and incrementState), is NOT available anymore. The designed way to modify the entity is via getBuilder().set... calls.

In addition, an automatic version management is introduced. For each applied event the version is automatically incremented. Direct version modification is not exposed as public API anymore.

For more details please see #466.

Entity Column Queries

It is now possible to use EntityColumns in the queries:

public class CustomerProjection 
    extends Projection<CustomerId, Customer, CustomerVBuilder> {

    // ...

    // Defines an entity column.
    public PersonName getContactName() {
        return getState().getContactPerson().getName();
    }
}

Then in your client code:

// Retrieve customers by a contact name.

// Obtain the parameters for the query from somewhere like user interface.
final PersonName contactName = getContactName();

// Create the `Query` instance.
final Query customerQuery = myRequestFactory.query()
                                            .select(Customer.class)
                                            .withMask("id", "address", "domain")
                                            .where(eq("contactName", contactName))
                                            .build();

// Get the client stub of the `QueryService`.
final QueryService queryService = getServerApi();

// Execute the query, setting the callback for results.
queryService.read(query, callback);

In the example above the results will contain customers, whose contactPerson.name value matches the one set in the query.

IntegrationBus

To integrate several BoundedContexts, an IntegrationBus is defined. It allows to subscribe to so called "external" events in one BoundedContext to receive events of a required type from another BoundedContext.

E.g. the first bounded context Projects defines an external event handler method in the projection as follows:

public class ProjectListView extends Projection<...> {

    @Subscribe(external = true)
    public void on(UserDeleted event) {
        // Remove the projects that belong to this user.
        // ...
    }

    // ...
}

Let's say the second bounded context is Users. In case these bounded contexts share the same transport, any UserDeleted event emitted inside of Users will also get delivered to ProjectListView of Projects bounded context. It will be done via IntegrationBus.

For more details see #580.

One-liner updates

  • Base data types and utilities have been extracted into base repository.
  • core module was introduced in the core-java repository; it defines classes and interfaces, that are essential for building application in the Event Sourcing paradigm.
  • Custom Protobuf options defined by the framework now take numbers in the range 73812-75000.
  • Failure is renamed to Rejection to comply with Reactive Manifesto definition of “Failure” better.
  • EndpointDelivery strategy is now available; it allows to define how messages are delivered to particular Entity instances.
  • Entity visibility options are added; VisibilityGuard is a registry of repositories that controls an access.

Version updates

  • Gradle 4.1.
  • Protobuf 3.3.0.
  • gRPC 1.4.0.

Version 0.9.0

03 May 12:49
Compare
Choose a tag to compare
Version 0.9.0 Pre-release
Pre-release

This version is a finalization of the framework API and most of the core features, that are scheduled for 1.0 release.

It also includes a lot of API polishing. Some of the changes are breaking, some are not.

The inner code structure (packaging, visibility of classes etc) has been modified significantly in order to follow the Onion architecture and become easier for understanding.

Below is a list of the most valuable changes and the references to the corresponding pull requests. More feature details are available upon the PR link.

  • Multitenancy support. Please see org.spine3.server.tenant package for the details.

Client-side features:

  • ActorRequestFactory is introduced as a single client-side utility to produce Commands, Queries and Topics: #431.
  • IDs now belong to Command, Event and Failure, not to their contexts: #450, #452.
  • An ability to define the custom Command attributes is introduced to the public API (#448). Also, Command now has the system-only properties hidden from the public access (#455).
  • ActorContext is now a unified context, reflecting the properties of an environment, where a Command, Query or Topic instance is created.
  • Fail-fast Command creation is available via ValidatingBuilders. They are automatically generated for each Command and serve to validate the field values while building Command messages.
  • Stand input parameters are now validated according to the Protobuf annotations set for the requests: #437.

Server-side changes:

  • It is now possible to @Subscribe to Failures: #362, #299.
  • FailureThrowable API simplification: #454.
  • Entity lifecycle:
    • repository views for “archived” and “deleted” items;
    • lifecycle flags: #294, #352, #371, #372.
  • Improvements to entity versioning: #346.
  • AggregatePart and AggregateRoot API improvements: #347.
  • Changes to Command dispatchers and handlers: #354, #358.
  • Simpler routing of commands for Process Managers: #313.
  • StorageFactory is made more configurable for different environments. In particular, it is now easier to provide different Storage implementations for testing and production deployment cases: #289, #290.
    It is now possible to store some of Entity fields separately to allow the further efficient querying: #398.
  • CommandStore and EventStorage are now Repositories, based upon RecordStorage: #394, #395.

Utilities:

  • StringifierRegistry is introduced to organize and simplify string-to-object and object-to-string conversion for Entity state objects and other user-defined types: #389, #382, #383, #378, #423, #436.
  • Improvements to Date/Time API: #387.

Lots of less valuable changes were made as well, including performance improvements, extended configurability of the framework building blocks, more helper utilities and object factories for testing.

Version updates:

  • gRPC 1.1.2 (#341).
  • Gradle 3.5.

New test dependencies:

  • Guava-testlib.

Version 0.8.0

31 Jan 19:43
Compare
Choose a tag to compare
Version 0.8.0 Pre-release
Pre-release

The update 0.8.0 is aimed to extend and polish the existing framework API, making the code less repetitive and more testable. We also tried to handle the most common scenarios to reduce the number of boilerplate code in applications built on top of Spine.

Aggregate API is extended

As learned from the real-world examples, sometimes the domain rules expressed within an Aggregate can become an overwhelming responsibility for a single class. That’s why AggregatePart and AggregateRoot abstractions are introduced to represent a large Aggregate as a set of smaller objects.

Such an approach also allows to access different parts of a composite Aggregate separately, improving the system performance.

Please see org.spine3.server.aggregate package for more details.

Improvements in the testing framework

To help building the unit tests faster and do not bother with the boilerplate code, new testing routines were introduced to org.spine3.test package. The improvements include:

  • easier testing of the commands and the entities which handle commands,
  • helpers for quick creation of Aggregate, AggregatePart, Projection, ProcessManager and Entity instances,
  • routines for nullability checks.

Repackaging of Storage implementations

Storage classes were moved to the same classes as related Protobuf types. Some of the methods were demoted from public to protected in order to make the API more clear.

Generated classes and interfaces are now available under storage sub-packages, and these packages are annotated as @SPI via package-info.java.

Public API improvements and changes

StorageFactorySwitch introduced

As of now it is possible to provide different StorageFactory implementations for production and testing modes in a unified way, taking care of the environment outside of the business and test logic.

The Supplier interface contract is used to allow lazy initialisation of the StorageFactory instances and improve the app startup performance:

final Supplier<StorageFactory> productionSupplier = …;
final Supplier<StorageFactory> testingSupplier = …;
// ...
StorageFactorySwitch.init(productionSupplier, testingSupplier).

BoundedContext contract changes

StorageFactory for a BoundedContext is now provided via lazy-init Supplier to speed up the context instantiation.

The BoundedContext.Builder API no longer requires StorageFactory instance. If it not set explicitly, the current StorageFactorySwitch value is used. So it’s now possible to create an instance as simple as

// Using the `StorageFactorySwitch` default.
final BoundedContext context = BoundedContext.newBuilder().build();

And one more change is made. Now the BoundedContext class made final to clearly state it is not designed for subclassing.

EventEnricher and EventStore configuration moved from BoundedContext to EventBus

In order to make the configuration more logical and avoid potential cross-dependencies, the configuration of the items used by the EventBus is now available via the EventBus.Builder, not via the BoundedContext.Builder:

// Spine 0.7.0:
BoundedContext.newBuilder()
              .setEventStore(customEventStore)
              .setEnricher(customEnricher)
              //… 
              .build();

// Spine 0.8.0:
final EventBus eventBus = EventBus.newBuilder()
                                  .setEventStore(customEventStore)
                                  .setEnricher(customEnricher)
                                  // …
                                  .build();
BoundedContext.newBuilder()
              .setEventBus(eventBus)
              // …
              .build();

More flexibility in event dispatching

A single Event can now be delivered to multiple dispatchers of the same type at once.

For instance, each projection repository can be configured to route the same incoming SomeEvent instance to a number of projections, managed by this repo. In this case each of the projection instances will receive exactly the same SomeEvent instance.

It is also possible to ignore the incoming Event by returning an empty set of target projection IDs. That may become handy to reflect a certain conditional behaviour of the read side.

This feature is available for both ProcessManagerRepository and ProjectionRepository. See EventDispatchingRepository.addIdSetFunction for details.

Dynamic configuration of the event enrichment process

Previously the EventEnricher must have been fully initialized by the moment of the BoundedContext creation. That made it hard to use the AggregateRepository instances in the enrichment functions, since AggregateRepository required the BoundedContext. There was a circular dependency:

EventEnricher (wants to use) —> AggregateRepo (requires) —> BoundedContext (requires) —> EventEnricher.

In order to break the circle, it is made possible to add the new enrichment functions at runtime:

boundedContext.getEventBus().addFieldEnrichment(...)

Event enrichment features

It is now possible to specify the whole package as a target for enrichment:

message EnrichmentForAllEventsInPackage {
     option (enrichment_for) = "com.foo.bar.*";
     ...
}

OR syntax became available in scope of by option. It is useful when the same enrichment is required for several events, but the anchor fields have different names:

package com.foo.bar;
// ...
message EventOne {
    UserId user_id = 1;
}

message EventTwo {
     UserId author_id = 1;
}

// …

message MyEnrichmentForEventOneAndTwo {
     option (enrichment_for) = “com.foo.bar.*”

     string name = 1 [(by) = “*.user_id | *.author_id”]
}

Postponed delivery of Events to the subscribers and dispatchers

It is now possible to define how and when the Event instances are delivered to their destinations. In particular, the event propagation can be postponed until the system performance allows to continue it.

It is especially handy for the cloud environments such as Google AppEngine. A custom strategy at a certain app node may be implemented to speed up the command propagation with no need to wait until the read-side becomes fully consistent. That allows to handle the incoming requests blazingly fast and thus scale better.

See setDispatcherEventDelivery and setSubscriberEventDelivery methods of EventBus.Builder.

Postponed delivery of Entity updates to Stand

In addition to the customisable Event delivery strategy, the same feature is available for Entity updates delivered to the Stand instance. Effective on per-BoundedContext basis, the behaviour can be set via BoundedContext.Builder.setStandUpdateDelivery. See StandUpdateDelivery documentation for details.

Valid option changes

Valid option has been simplified and is now a bool instead of Message.

// Spine 0.7.0:
MyMessage field = 1[(valid).value = true]

// Spine 0.8.0:
MyMessage field = 1[(valid) = true]

EntityRepository has been renamed to RecordBasedRepository

This is done to reflect the class nature better.

Various improvements and fixes

  • UnexpectedTypeException is thrown in response to packing/unpacking the Protobuf values instead of plain RuntimeException as previously.
  • Issue #255 fixed: return Optional on load() in Repository instances.
  • Identifiers utility class has been refactored, with a object-to-String conversion routines split into the separate Stringifiers utility class.
  • Mismatches improvements: handle the case when a command expects some value and wants to change it, but discovers a default Protobuf Message value.
  • The recommended visibility level for the command handler methods was changed to default to make the Aggregate API cleaner.
  • A utility Tests.hasPrivateUtilityConstructor() method has been renamed to Tests.hasPrivateParameterlessCtor() to better reflect broader usage.

Dependency updates:

  • Gradle 3.3.

Version 0.7.0

30 Dec 16:18
Compare
Choose a tag to compare
Version 0.7.0 Pre-release
Pre-release

New artifact IDs for Core Java modules

Gradle:

repositories {

   // Spine releases repository.
   maven { url = 'http://maven.teamdev.com/repository/spine' }

}

dependencies {

    compile 'org.spine3:spine-server-core:0.7.0'
    compile 'org.spine3:spine-client-core:0.7.0'

    compile 'org.spine3:spine-users:0.7.0'
    compile 'org.spine3:spine-values:0.7.0'

    testCompile 'org.spine3:spine-testutil-core:0.7.0'
}

Handling changes and mismatches of values

This version introduces Protobuf types for describing field changes. Examples are StringChange, TimestampChange, Int64Change, etc. For more types, please see the spine/change/change.proto file.
These types are for being used in commands (to request a change) and events (to describe the change made).

The ValueMismatch type allows to fire a failure when an entity already had a value of a field, which differs from one described as a previousValue of corresponding change request. Please also see Java utility classes in the org.spine3.change package for details.

Wildcard (by) option was introduced for enrichments

Now a single enrichment message can be used with multiple target events. E.g.

message EnrichmentForSeveralEvents {
     string username = 1 [(by) = "*.user_id"];
}

message EventOne {
     option (enrichment) = "EnrichmentForSeveralEvents";

     int32 user_id = 1;
}

message EventTwo {
     option (enrichment) = "EnrichmentForSeveralEvents";

     int32 user_id = 1;
}

Behavioural changes and improvements

Automatic ProjectionRepository catch-up after #initStorage

As we discovered from the real-world cases, the catch-up for the ProjectionRepository is typically initiated right after the storage factory is set. E.g.

// Spine 0.6.0: 
repository.initStorage(storageFactory);
repository.catchUp()

Now the default behaviour is to invoke #catchUp automatically. So less code, less mistakes. E.g.

// Spine 0.7.0: 
repository.initStorage(storageFactory);

Of course, you can always turn off the automatic catch-up passing false to the ProjectionRepository constructor.

ProcessManager changes

ProcessManagers may now have no commands handled. So it is now possible to build the process on top of events only.

Syntax sugar for type names

Some sugar is now available to get the type name for a Message. E.g.

// Spine 0.6.0:
String name =  TypeUrl.of(msg).getTypeName();

// Spine 0.7.0.
String name = TypeName.of(msg);

System and runtime improvements

Aggregate state double-checking upon update

Improvements were made to keep the Aggregate state consistent across different threads or even scaled instances of the application. Now Aggregate repository checks that no new events have arrived to the event storage since the start of the command processing.

In case any new events are detected, the processing of this command is repeated.

CommandBus configuration

CommandBus can now be configured to avoid spawning new threads during the bus operation. It is helpful for sensitive target runtimes such as Google AppEngine. E.g.

final CommandBus commandBus = CommandBus.newBuilder()
                                        // Allowed by default. Let's disable.
                                        .setThreadSpawnAllowed(false)
                                        .build();

Core singletons made thread-safe

ConverterRegistry and DefaultStateRegistry made thread-safe.

Validation

Goes option introduced

Declarative validation rules for the Protobuf model were extended with (goes) option. It is now possible to declare a message field that can only be present if another field is present. E.g.

message ScheduledItem {
     ...
     spine.time.LocalDate date = 4;
     spine.time.LocalTime time = 5 [(goes).with = "date"];
}

Required option simplified

(required) Protobuf option has been simplified and is now a bool instead of a RequiredOption message. E.g.

// Spine 0.6.0:
MyMessage field = 1 [(required).value = true];

// Spine 0.7.0.
MyMessage field = 1 [(required) = true];

Structural Changes:

  • CommandBus made configurable via CommandBus.Builder instead of CommandBus#newInstance.

General Improvements

  • Date/Time API improvements.
  • Entity version is now propagated to Stand upon an update. That allows to store the version properly in StandStorage.

Dependency Updates

  • Guava 20.0
  • gRPC 1.0.2
  • Protobuf 3.1.0

And the last but not least:

Codacy Badge

Spine is now integrated with Codacy, exposing the code quality metrics to everyone.

Version 0.6.0

14 Oct 14:13
Compare
Choose a tag to compare
Version 0.6.0 Pre-release
Pre-release

Features & Improvements

Version 0.6.0 introduces a main read-side concept — Stand.

It provides query and subscription API on per-BoundedContext basis, acting as a facade for data retrieval. Being downstream with the Repositories, Stand also stores the latest Aggregate states, making the state fetch much more efficient.

Application interface has been extended with a couple of read-side oriented gRPC services:

  • QueryService — enabling synchronous state retrieval;
  • SubscriptionService — allowing to subscribe for entity state changes.

Structural Changes:

  • ClientService has been renamed toCommandService; its subscription-related methods were moved to the newly introduced SubscriptionService.
  • Model definitions and services related to Users domain were moved to a separate users module.
  • Examples have been extracted as a separate repository.

Dependency Updates

  • gRPC 1.0.0.
  • Protobuf Gradle Plugin 0.8.0

Version 0.5

08 Aug 14:09
Compare
Choose a tag to compare
Version 0.5 Pre-release
Pre-release

Spine Version 0.5 is all about event enrichments.

Features & Improvements

  • In this release we introduced Event enrichments - extra information of an event based on its type and content. The mechanism for enriching messages is based on FieldOptions of Protobuf message definitions. Just mark an enrichment message with enrichment_for annotation, its fields - with by annotation, and register functions which would transform corresponding event (or context) fields to enrichment fields. There can be multiple enrichments for an event, and one enrichment can be used for multiple events.
  • It is now required to define type_url_prefix Protobuf file option in all .proto files, so that Spine can use your Protobuf types properly.
    *As direct imports make dependency analysis much easier, we removed public imports in all .proto files.
  • In this version, we excluded slf4j-jdk14 from the published dependencies and now use it only for tests. As a Library, we provide logging facade API, not specific logger binding. Target apps are free to use any binding they need.
  • Starting from 0.5, an ability to use custom Connection Pool or Data Source is added in JDBC-based storage implementation. Also, it is possible to customize all the DB tables and queries easily, by overriding them.
  • It is possible to configure Spine Gradle Protobuf plugin with a custom target, source, and other paths.
  • Few important classes were covered with tests. Overall test coverage increased by 2.5%.

Below let us list the issues that, however, no longer endanger your development process.

  • Fixed invalid entries in known_types.properties.
  • Issues with error messages were fixed.

Dependency Updates

As always, we strive for the latest and greatest. With the new release we updated to:

  • gRPC 1.0.0-pre2
  • Protobuf 3.0.0

v0.4.4

13 Jun 15:37
Compare
Choose a tag to compare
v0.4.4 Pre-release
Pre-release
  • Extended MessageOptions for annotating commands with events and failures.
  • Added ValueMistatch message type for easier declaration of failures related to data mutation.

Version 0.4.2

10 Jun 16:31
Compare
Choose a tag to compare
Version 0.4.2 Pre-release
Pre-release

Features & Improvements

  • Starting from this version we added an ability to import events via special command. This may be useful for:
    • importing data via transforming it to aggregate events (as facts in the past);
    • integrating with external data sources.
  • Following the best practices, now we use StreamObserver.onError() instead of returning responses while posting integration events. The same approach is used in CommandBus.
  • New custom Protobuf options were introduced: internal, SPI, experimental and beta. They are to be used as the similar Java annotations.

Improvements always come together with some new wisdom and old issues correction. Here is the one for release 0.4.2.

  • Rescheduling commands from the storage now happens in parallel on CommandBus creation. This way we avoid blocking of the client code execution.

Version 0.4.1

07 Jun 18:57
Compare
Choose a tag to compare
Version 0.4.1 Pre-release
Pre-release

Core Java

Features & Improvements

  • Starting from 0.4.1 version Spine supports integration events. An integration event is sent between loosely coupled parts of a system. BoundedContext now is IntegrationEventSubscriber and it is possible to post integration events using BoundedContext.
  • As events may have multiple subscribers that register and “listen” them, in the new release we renamed event handlers to event subscribers.
  • Now you can use a target version attribute of the CommandContext. The attribute indicates a version of the entity for which this command is intended.
  • The context parameter in event subscribers and command handlers used to be required in the previous versions of the framework. Now it is optional.
  • The ProjectionRepository catch-up is now supported. So, once switched to the 0.4.1 version of the framework, you will be able to update projections from the event stream obtained from the EventStore.
  • If you would like to gather statistics on errors and invalid commands, you can do so with the new release. Invalid and unsupported commands are stored with the error status to make further analysis.
  • In this version, we interjected a wrapper that tames the inscrutable and continuously changing cloud application — the ClientService — facade for client apps. Create a ClientService using builder with multiple BoundedContexts and use it to post commands.
  • This is fairly cool, we made Storages multi-tenant! To make multi-tenancy obvious, use TenantId instead of Namespacefor multi-tenancy support.
  • When you are writing or rewriting an enterprise application, there are going to be parts of the system that facilitate the business but are not core to the business. For example, in most businesses there is a concept of tracking time. That is why we added a new type definition in Protobuf — Work. This type represents the amount of work done in minutes.
  • Similarly to Work type, we added URL definition in the Protobuf. The URL can be defined in raw string form or as a structured parsed record. To support the new type there is a QueryParameters — the utility class, which helps to perform URL query parameters parsing and String conversion. And one more utility class — Urls, which simplifies working with URL. It provides all necessary operations, such as conversion and validation.
  • To make current time obtaining more abstract and do not use the one from the Protobuf, you can publish the testutil project and add Provider interface.

Below let us list the issues that, however, no longer endanger your development process.

  • We do care about all your events and do not want to lose them anymore. Therefore, we made Repositories post events that were produced as a result of the command dispatching and CommandBus to post events generated by command handlers.
  • The automatic aggregate snapshots creation did not work as intended in the previous version. Now it is fixed.
  • The server is restarted? This is not a reason to stop posting already scheduled commands. Now, if there are any scheduled commands found on the CommandBus startup, they are re-scheduled again.
  • We thought you would enjoy the example of the Aggregate with all new features described. So, we fixed it and improved a bit.

Dependency Updates

As always, we strive for the latest and greatest. With the new release we updated to:

  • gRPC 0.14.0
  • Protobuf 3.0.0 beta-3.
  • Gradle 2.13.