Skip to content
/ dachs Public

Dachs - Data Change Snitch - Unified persistence events

License

Notifications You must be signed in to change notification settings

ethlo/dachs

Repository files navigation

Dachs - Data Change Snitch

Build Status Maven Central Hex.pm Codacy Badge

A unified entity change-listener across different persistence APIs and implementations.

Rationale

In almost all back-end systems there is the need to notify or update additional data whenever core data changes. It can be sending a notification event, invalidating cache entries, audit logging or updating a search index to mention a few.

For example Spring has already support for events, and from Spring 4.2 it also has support for send-at-end-of-transaction-events. Further Spring JPA supports auditing, however it does not support fetching the actual data, just who changed it and when. There is no recollection of what was changed.

Dachs flow

All the different persistence frameworks have their different APIs for detecting data changes. With Dachs you have one simple, unified API to deal with.

Maven artifact

<dependency>
  <groupId>com.ethlo.dachs</groupId>
  <artifactId>dachs-{impl}</artifactId>
  <version>${dachs.version}</version>
</dependency>

Supported persistence frameworks

API

The goal is to have a simple, but powerful API to get notifications of all changes to entities, that is created, updated and deleted.

Transactional listener

This listener will buffer all events until the transaction is about to commit. preDataChanged is called just before the transaction is committed. Any exception here will abort the transaction. postDataChanged is called just after the transaction is committed.

public interface EntityChangeSetListener
{
  void preDataChanged(EntityDataChangeSet changeset);
  void postDataChanged(EntityDataChangeSet changeset);
}

Both methods gives you an EntityDataChangeSet whichs holds all operations performed inside the transaction.

public interface EntityDataChangeSet
{
  List<EntityDataChange> getCreated();
  List<EntityDataChange> getUpdated();
  List<EntityDataChange> getDeleted();
  boolean isEmpty();
}
Non-transactional listener

EntityChangeListener

public interface EntityChangeListener
{
  void preCreate(EntityDataChange entityData);
  void preUpdate(EntityDataChange entityData);
  void preDelete(EntityDataChange entityData);
  void created(EntityDataChange entityData);
  void updated(EntityDataChange entityData);
  void deleted(EntityDataChange entityData);
}

Using this simple listener, we get an EntityDataChange object for each operation on the entity.

Common for both trasactional and non-transactional
public interface EntityDataChange
{
  Serializable getId();
  Object getEntity();
  List<PropertyChange<?>> getPropertyChanges();
  Optional<PropertyChange<?>> getPropertyChange(String propertyName);
}

Each EntityDataChange object holds a collection of PropertyChanges that is the individual properties that has changed.

public interface PropertyChange<T>
{
  String getPropertyName();
  Class<T> getPropertyType();
  T getOldValue();
  T getNewValue();
}

Example output

Given a simple Person object:

public class Person()
{
  private String name;
  private Integer age;
}
Created
EntityData
  propertyChanges:
    * name - null => "John Doe"
    * age - null => 34
Updated
EntityData
  propertyChanges:
    * name - "John Doe" => "John Smith"
    * age - 34 => 47
Deleted
EntityData
  propertyChanges:
    * name - "John Smith" => null
    * age - 47 => null

Limitations

Dachs relies on the persistence framework in use to notify about operations and there might be limitations. In general bulk delete will not trigger delete events (aka DELETE FROM Entity).