Managing Bitemporal Data With BarbelHisto

DZone 's Guide to

Managing Bitemporal Data With BarbelHisto

Avoid the technical baggage that can come with managing scheduled or bitemporal data with this helpful library.

· Open Source Zone ·
Free Resource

The last 15 years I've worked on projects for insurance businesses, implementing a variety of policy management systems. The topic that has always been a major requirement was to store the policies and their changes in a way that is traceable for audits and customer claims. Implementing bullet-proof bitemporal data storage has always taken a reasonable amount of time (and nerves). Every time we've implemented a new policy management system we have been on the lookout for a reusable component for bitemporal data. The few options we found did not really satisfy our needs, or had too many technical constraints. For that reason I've decided to implement my own open source library that I'd like to share with you guys: BarbelHisto. With this lightweight library I want to address that bitemporal data storage requirement, without any bothersome constraints. Just managing bitemporal data, that's it. No technology backpack. 

Before I dive into  BarbelHisto, let's briefly introduce "bitemporal" data.  Bitemporal data storage means that all your business data is stored along two time dimensions. Martin Fowler has described this pattern here. Record time is the time when a record state was created and deleted (or inactivated) in a system. Effective time is when a certain state of business data is supposed to become effective. For instance, a client communicates an address change two weeks in advance of his removal. With bitemporal functionality in your system, you can store that address change today (record time) and make it effective in two weeks (effective time). There are many businesses like insurance companies that have to store data in bitemporal format to track all their changes to data. Notice that  BarbelHisto is not just a simple audit trail; it stores record time (like common audit trails do) and effective time.

Now let's dive into  BarbelHisto. Let's demonstrate how BarbelHisto  works to make bitemporal data happen. To create an instance of  BarbelHisto  you simply type:

BarbelHisto<Client> barbel = BarbelHistoBuilder.barbel().build();

By default,  BarbelHisto  will run in  BarbelMode.POJO. This is the Client POJO:

public static class Client {
   private String clientID;
   private String firstname;
   private String lastname;
   private List<Adress> adresses = new ArrayList<>();
   // ... constructors and accessors

You need to annotate the primary key for your business class with  @DocumentId . Clients are identified by their unique client ID. This ID will become the identifier of the document journal in BarbelHisto. Now you can store clients in the BarbelHisto  instance like so:

Client client = new Client("1234", "Niklas", "Schlimm");
barbel.save(client, LocalDate.now(), LocalDate.MAX);

This record will be effective today and last forever with   createdAt = today . Let's make an address change in two weeks. Say that's March 1st, 2019.

Client effectiveNow = barbel.retrieveOne(BarbelQueries.effectiveNow("1234"));
effectiveNow.getAdresses().add(new Adress("Barbel Street 10", "Houston"));
barbel.save(effectiveNow, LocalDate.of(2019, 3, 1), LocalDate.MAX);

That's all. BarbelHisto  will now have two active versions of that client. One starting today and one starting in two weeks, we've assumed "in two weeks" means March 1st. Notice that BarbelHisto  always makes snapshots of versions saved and also copies when clients retrieve data. This adds a lot of convenience and safety when developers manipulate objects in their data processing.  BarbelHisto allows you two export the journal in pretty format so you know what's goning on internally. Here's the print out of our example:

Document-ID: 1234

|Version-ID                              |Effective-From |Effective-Until |State   |Created-By           |Created-At                                   |Inactivated-By       |Inactivated-At                               |Data                           |
|bbbb2dcc-1f9d-4698-9f86-97443636d2ad    |2019-02-18     |2019-03-01      |ACTIVE  |SYSTEM               |2019-02-18T15:46:58.298+01:00[Europe/Berlin] |NOBODY               |2199-12-31T23:59:00Z                         |EffectivePeriod [from=2019-02- |
|2cd17867-c75f-48c4-860e-02f5ead7e560    |2019-02-18     |999999999-12-31 |INACTIVE|SYSTEM               |2019-02-18T15:46:58.169+01:00[Europe/Berlin] |SYSTEM               |2019-02-18T15:46:58.298+01:00[Europe/Berlin] |EffectivePeriod [from=2019-02- |
|84ed4f75-68d8-4bb2-b3b6-6d6534a7490c    |2019-03-01     |999999999-12-31 |ACTIVE  |SYSTEM               |2019-02-18T15:46:58.296+01:00[Europe/Berlin] |NOBODY               |2199-12-31T23:59:00Z                         |EffectivePeriod [from=2019-03- |

As you can see, there are three versions in total. Two of them are active, one active from February 18th when I prepared this article, and one version effective from March 1st. The inactive version is the initial version we've stored which was effective from February 18th and does not expire. This version is inactivated because you posted an update that crossed the effective time of that initial version. In BarbelHisto , nothing will ever be deleted, so the old version just gets inactivated.  

That's it for now. The complete code can be seen here in this small test case. In my next articles, I present some of the more sophisticated features of BarbelHisto, like persistence, timeshifting, transactions, event handling and so forth. I hope you'll like that little library I brought to light. I appreciate all your feedback and contributions on my github account

bitemporal data, data, event management, java, open source

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}