No, you are not about to be invited to purchase some fissile material of any isotopic constitution whatsover.
Plutonium is a software component that:-
- Stores data that models things in the real world; this is object oriented data, the sort that Java applications deal with. POJOs.
- Does so in a way that tracks the historical evolution of those things in the real world, and the consequences. What things and what consequences? Say, your electricity consumption and the consequent bill; the location of a delivery van filled with boxes of your products, who is in possession of those products and whether sales have been realised; the contracts managed by a trading desk how much money a the desk might owe if the GBP/EUR FX rate might drop by 1%.
- Tracks relationships between things that can change, and the consequences. For account, what accounts an electricity provider has on its books, what lorries are carrying what things, what trades are being managed in a portfolio.
- Supports queries about these things from the point of view of some instant in time - at the current time, at some point back in the past, or extrapolated to some point in the future.
- Allows the historical record to be revised, reflecting that prior knowledge may have been inaccurate or incomplete. Queries reflect both a time in the real world, and a revision of our knowledge about the world.
- Supports 'what-if' scenarios where alternate experimental histories can be branched off a common base history, including multiple branches. Good for risk cubes in the finance sector and simulation.
It takes a CQRS approach to doing this, working off imperative events and furnishing a purely functional query API. It is intended for use by both Java and Scala projects.
Plutonium does not:-
- Require any kind of interface definition language for its data.
- Need mapping files or configuration to bridge between Java objects and the data store.
- Use code generation or other fancy tools - plain old Java is all you need.
- Bother the application writer using it with lifecycle management for those objects.
- Want to take over your application architecture - it is just a library that you use, not a framework that dictates how your application is structured.
It is published via Bintray to JCenter.
Add this to your build.sbt:
resolvers += Resolver.jcenterRepo
libraryDependencies += "com.sageserpent" %% "plutonium" % "1.3.0"
Add this to your build.gradle:
repositories {
jcenter()
}
dependencies {
compile 'com.sageserpent:plutonium_2.12:1.3.0'
}
jshell> /reload
| Restarting and restoring state.
-: import com.sageserpent.americium.NegativeInfinity;
-: import com.sageserpent.americium.Unbounded;
-: import com.sageserpent.plutonium.World;
-: import com.sageserpent.plutonium.WorldReferenceImplementation;
-: import com.sageserpent.plutonium.javaApi.*;
-: import java.time.Instant;
-: World<Integer> world = new WorldReferenceImplementation<>();
-: NegativeInfinity<Instant> atTheBeginningOfTime = NegativeInfinity.apply();
-: {
final Instant asOf = Instant.now();
final int eventId = 0;
world.revise(eventId, Change.forOneItem(atTheBeginningOfTime, "Fred", Account.class, accountItem -> {
accountItem.setCash(5.0);
}), asOf);
final int followingRevision = world.nextRevision();
final Scope scope = world.scopeFor(atTheBeginningOfTime, followingRevision);
final Account account = scope.render(Bitemporal.withId("Fred", Account.class)).head();
System.out.println(followingRevision);
System.out.println(atTheBeginningOfTime);
System.out.println(account.getCash());
}
1
NegativeInfinity()
5.0
-: Instant toStartWith = Instant.ofEpochSecond(0);
-: int rememberThisEventId = 1;
-: {
final Instant asOf = Instant.now();
world.revise(rememberThisEventId, Change.forOneItem(toStartWith, "Fred", Account.class, accountItem -> {
accountItem.setCash(3.8);
}), asOf);
final int followingRevision = world.nextRevision();
final Scope scope = world.scopeFor(toStartWith, followingRevision);
final Account account = scope.render(Bitemporal.withId("Fred", Account.class)).head();
System.out.println(followingRevision);
System.out.println(toStartWith);
System.out.println(account.getCash());
}
2
1970-01-01T00:00:00Z
3.8
-: Instant oneHourLater = toStartWith.plusSeconds(3600L);
-: {
final Instant asOf = Instant.now();
final int eventId = 2;
world.revise(eventId, Change.forOneItem(oneHourLater, "Fred", Account.class, accountItem -> {
accountItem.setCash(6.7);
}), asOf);
final int followingRevision = world.nextRevision();
final Scope scope = world.scopeFor(oneHourLater, followingRevision);
final Account account = scope.render(Bitemporal.withId("Fred", Account.class)).head();
System.out.println(followingRevision);
System.out.println(oneHourLater);
System.out.println(account.getCash());
}
3
1970-01-01T01:00:00Z
6.7
-: Instant twoHoursLater = oneHourLater.plusSeconds(3600L);
-: {
final Instant asOf = Instant.now();
final int eventId = 3;
world.revise(eventId, Annihilation.apply(twoHoursLater, "Fred", Account.class), asOf);
final int followingRevision = world.nextRevision();
System.out.println(followingRevision);
System.out.println(twoHoursLater);
}
4
1970-01-01T02:00:00Z
-: {
final Instant asOf = Instant.now();
world.revise(rememberThisEventId, Change.forOneItem(toStartWith, "Fred", Account.class, accountItem -> {
accountItem.setCash(3.0);
}), asOf);
final int followingRevision = world.nextRevision();
System.out.println(followingRevision);
System.out.println(toStartWith);
}
5
1970-01-01T00:00:00Z
-: {
final int followingRevision = 0;
final Scope scope = world.scopeFor(twoHoursLater, followingRevision);
System.out.println(followingRevision);
System.out.println(twoHoursLater);
System.out.println(scope.render(Bitemporal.withId("Fred", Account.class)).isEmpty());
}
0
1970-01-01T02:00:00Z
true
-: {
int followingRevision = 1;
final Scope scope = world.scopeFor(atTheBeginningOfTime, followingRevision);
final Account account = scope.render(Bitemporal.withId("Fred", Account.class)).head();
System.out.println(followingRevision);
System.out.println(atTheBeginningOfTime);
System.out.println(account.getCash());
}
1
NegativeInfinity()
5.0
-: {
final int followingRevision = 2;
final Scope scope = world.scopeFor(atTheBeginningOfTime, followingRevision);
final Account account = scope.render(Bitemporal.withId("Fred", Account.class)).head();
System.out.println(followingRevision);
System.out.println(atTheBeginningOfTime);
System.out.println(account.getCash());
}
2
NegativeInfinity()
5.0
-: {
int followingRevision = 3;
{
final Scope scope = world.scopeFor(atTheBeginningOfTime, followingRevision);
final Account account = scope.render(Bitemporal.withId("Fred", Account.class)).head();
System.out.println(followingRevision);
System.out.println(atTheBeginningOfTime);
System.out.println(account.getCash());
}
{
final Scope scope = world.scopeFor(twoHoursLater, followingRevision);
final Account account = scope.render(Bitemporal.withId("Fred", Account.class)).head();
System.out.println(followingRevision);
System.out.println(twoHoursLater);
System.out.println(account.getCash());
}
}
3
NegativeInfinity()
5.0
3
1970-01-01T02:00:00Z
6.7
-: {
int followingRevision = 4;
final Scope scope = world.scopeFor(twoHoursLater, followingRevision);
final Iterable<Account> accountIterable = scope.renderAsIterable(Bitemporal.withId("Fred", Account.class));
System.out.println(followingRevision);
System.out.println(twoHoursLater);
System.out.println(accountIterable.iterator().hasNext());
}
4
1970-01-01T02:00:00Z
false
-: {
final int followingRevision = 5;
final Scope scope = world.scopeFor(toStartWith, followingRevision);
final Account account = scope.render(Bitemporal.withId("Fred", Account.class)).head();
System.out.println(followingRevision);
System.out.println(toStartWith);
System.out.println(account.getCash());
}
5
1970-01-01T00:00:00Z
3.0