Domain Configuration

None of us like to configure tools but don’t worry — JaVers knows it and
does the hard work to minimize the configuration efforts on your side.

As we stated before, JaVers configuration is very concise.
You can start with zero config and give JaVers a chance to infer all the facts about your domain model.

Take a look how JaVers deals with your data. If it’s fine, let the defaults work for you.
Add more configuration when you want to change the default behavior.

There are two logical areas of the the configuration,
domain model mapping
and
repository setup.
Proper mapping is important for both JaVers features, the object diff and the data audit (JaversRepository).

The object diff algorithm is the core of JaVers. When two objects are compared, JaVers needs to know what
type they are.
We distinguish between the following types: Entities, Value Objects, Values, Containers, and Primitives.
Each type has a different comparing style.

JaVers can infer the type of your classes, but if it goes wrong, the diff result might be strange.
In this case you should tune the type mapping.

For now, we support both the Java config via JaversBuilder
and the annotations config.

Domain model mapping

Why domain model mapping is important?
Many frameworks which deal with user domain model (aka data model) use some kind of mapping.
For example JPA uses annotations in order to map user classes into a relational database.
Plenty of XML and JSON serializers use various approaches to mapping, usually based on annotations.

When combined together, all of those framework-specific annotations could be a pain and a
pollution in your business domain code.

Mapping is also a case in JaVers but don’t worry:

JaVers wants to know only a few basic facts about your domain model classes.

Mapping is done mainly on the class level — property level mapping is required only for
choosing Entity ID.

JaVers uses reasonable defaults and takes advantage of a type inferring algorithm.
So for a quick start just let JaVers do the mapping for you.
Later on, it would be advisable to refine the mapping in order to optimize the diff semantic.

Proper mapping is essential for the diff algorithm, for example JaVers needs to know if a given object
should be compared property-by-property or using equals().

JaVers Types

The JaVers type system is based on Entity and Value Objects notions, following Eric Evans
Domain Driven Design terminology (DDD).
Furthermore, JaVers uses Value, Primitive, and Container notions.
The last two types are JaVers internals and can’t be mapped by a user.

ProTip: The JaversType of an ID property type is mapped automatically
to Value if it’s an Object or to Primitive if it’s a Java primitive.
So — technically — you can’t have a Value Object type here.

ProTip: JaVers doesn’t distinguish between JPA @Id and @EmbeddedId annotations,
so you can use them interchangeably.

Value Object

JaVers ValueObject is similar to DDD Value Object and JPA Embeddable.
It’s a complex value holder with a list of mutable properties but without a unique identifier.

Value Object instances has a ‘best effort’ global identifier called
ValueObjectId.
It consists of the owning Entity InstanceId and
the path in an object subtree.

Comparing strategy for Value Objects is property-by-property.

ProTip: In a strict DDD approach, Value Object can’t exist independently and has to be bound to an Entity instance
(as a part of an Aggregate). JaVers is not so radical and supports both embedded and dangling Value Objects.
So in JaVers, Value Object is just an Entity without identity.

Example Value Object class:

// Value Object is the default type in Javers, so @ValueObject annotation can be omittedpublicclassAddress{privatefinalStringcity;privatefinalStringstreet;privatefinalStringzipCode;publicAddress(Stringcity,Stringstreet,StringzipCode){this.city=city;this.street=street;this.zipCode=zipCode;}// Value Object instances are compared property-by-property,// so the Object.equals() method is ignored by Javers@Overridepublicbooleanequals(Objecto){...}}

Value

Javers Value
is a simple value holder.
A Value class could have more than one field, but they are treated as an internal state.
For the rest of the world, a Value is a … single value.
A few examples of well-known Value types: BigDecimal, LocalDate, String, URL.

Comparing strategy for Values is (by default) based on Object.equals().
So it’s highly important to implement this method properly,
it should compare the underlying state of given objects.

Values defined in java.* packages, like BigDecimal or LocalDate have it already.
Remember to implement equals() for all your Value classes.

If you don’t control the Value implementation,
you can still change the comparing strategy by registering a
CustomValueComparator.

For example, if you want to compare BigDecimals rounded to two decimal places:

Implicitly, using the type inferring algorithm based on the class inheritance hierarchy.
JaVers propagates the class mapping down to the inheritance hierarchy.
If a class is not mapped (by method 1 or 2),
JaVers maps this class the in same way as its nearest supertype (superclass or interface)

Use the defaults. By default, JaVers maps a class to Value Object.

PrioritiesJaversBuilder.register...() methods have a higher priority
then annotations — JaVers ignores type annotations when a class is already
registered in JaversBuilder.
Type inferring algorithm has the lowest priority.

Mapping ProTips

First, try to map high level abstract classes.
For example, if all of your Entities extend some abstract class,
you can map only this class using @Entity.

JaVers automatically scans JPA annotations
and maps classes with @javax.persistence.Entity as Entities
and classes with @javax.persistence.Embeddable as Value Objects.
So if you are using frameworks like Hibernate, your mapping is probably almost done.

Use @TypeName
annotation for Entities, it gives you freedom of class names refactoring.

In some cases, annotations could be misleading for JaVers.
For example Morphia framework uses @Entity annotation for each persistent class
(even for Value Objects). This could cause incorrect JaVers mapping.
As a solution, use the JaversBuilder.register...()
methods, as they have higher priority than annotations.

For an Entity, a type of its Id-property is mapped as Value by default.

If JaVers knows nothing about a class — defaulted mapping is Value Object..

@Value
—
declares a given class (and all its subclasses) as the Value type.

@DiffIgnore
—
declares a given class as totally ignored by JaVers.
All properties with ignored type are ignored.
Use it for limiting depth of object graphs to compare. See ignoring things.

@ShallowReference
—
declares a given class as the Shallow Reference type.
It’s a tricky variant of the Entity type with all properties except Id ignored.
Use it as the less radical alternative to @DiffIgnore
for limiting depth of object graphs to compare. See ignoring things.

@IgnoreDeclaredProperties
—
use it to mark all properties declared in a given class as ignored by JaVers.
JaVers still tracks changes done on properties inherited from a superclass.
See ignoring things.

@TypeName
—
a convenient way to name Entities and Value Objects.
We recommend using this annotation for all Entities and Value Objects.
Otherwise, Javers uses fully-qualified class names
in GlobalIds, which hinders refactoring classes committed to JaversRepository.
Important! All classes with
@TypeName
should be registered in
JaversBuilder
using withPackagesToScan(String) or javers.packagesToScan Spring Boot starter property.

Three JPA Class level annotations are interpreted as synonyms of JaVers annotations:

Property level annotations

@DiffInclude
—
declares a property as visible by JaVers. Other
properties in a given class are ignored (unless explicitly included).
Including is opposite approach to Ignoring, like blacklisting vs whitelisting.
You can use only one approach for a given class.

@ShallowReference
—
declares a property as ShallowReference.
Can be used only for Entity type properties.
All properties of a target Entity instance, except Id, are ignored.

@PropertyName
—
gives an arbitrary name to a property.
When not found, Javers infers a property name from a field or getter name.
@PropertyName could be useful when refactoring clases that are already
committed to JaversRepository.

ProTip: when property level @Id is found in a class, JaVers maps it automatically
as Entity. So when you use @Id, class level @Entity is optional.

Two JPA property level annotations are interpreted as synonyms of JaVers annotations:

MongoStoredEntity class as Entity, since the @Id annotation is present,

ObjectId class as Value, since it’s the type of the ID property and it’s not a Primitive.

So far so good. This mapping is OK for calculating diffs.
Nevertheless, if you plan to use JaversRepository,
consider providing custom JsonTypeAdapter
for each of yours Value types,
especially Value types used as Entity ID (see JSON TypeAdapters).

Property mapping style

There are two mapping styles in JaVers FIELD and BEAN.
FIELD style is the default one. We recommend not changing it, as it’s suitable in most cases.

In both styles, access modifiers are not important, it could be private ;)

Ignoring things

The ideal domain model contains only business relevant data and no technical clutter.
Such a model is compact and neat. All domain objects and their properties are important and worth being persisted.

In the real world, domain objects often contain various kind of noisy properties you don’t want to audit,
such as dynamic proxies (like Hibernate lazy loading proxies), duplicated data, technical flags,
auto-generated data and so on.

It’s important to exclude these things from the JaVers mapping, simply to save the storage and CPU.
This can be done by marking them as ignored.
Ignored properties are omitted by both the JaVers diff algorithm and JaversRepository.

Sometimes ignoring certain properties can dramatically improve performance.
Imagine that you have a technical property updated every time an object is touched
by some external system, for example:

publicclassUser{...// updated daily to currentdate, when object is exported to DWHprivateTimestamplastSyncWithDWH;...}

Whenever a User is committed to JaversRepository,
the lastSyncWithDWH property is likely to cause a new version of the User object, even if no important data are changed.
Each new version means a new User snapshot persisted to JaversRepository
and one more DB insert in your commit.