Search code examples
javers

Getting a Commit returned after a commit with no changes


If I commit the same entity twice, the first time with changes and the second time without any changes, I receive a Commit with a CommitId both times. The first one holds the changes, the second has a empty changes list.

Is this behaviour intentional? I would expect to not get a CommitId for the second commit as there is no change and also no commit in the database. I got around the issue by checking if the changes list is not empty.

My Repository

public class CustomerRepository {

    private final Javers javers;

    CustomerRepository(Javers javers) {
        this.javers = javers;
    }

    public Optional<CommitId> save(Customer customer, Metadata metadata) {
        try {
            var author = Optional.ofNullable(metadata.author())
                    .orElse("unknown");

            Commit commit = javers.commit(author, customer); //<-- this returns a Commit with a CommitId

            return Optional.of(commit.getId());
        } catch (Exception exception) {
            log.warn("Couldn't commit customer", exception);
        }
        return Optional.empty();
    }

    public ChangesByCommit getCommitForCustomer(CustomerId customerId, CommitId commitId) {
        var query = QueryBuilder
                .byInstanceId(customerId.getValue(), Customer.class)
                .withCommitId(commitId)
                .withChildValueObjects()
                .build();
        return javers.findChanges(query).groupByCommit().stream().findFirst().orElseThrow(() -> new CommitNotFoundException(customerId, commitId));
    }
}

and my test case would be this

    @Test
    void emptyCommit() {
        var customer = new Customer(new CustomerId("id"));
        var metadata = new Metadata("author");
        Optional<CommitId> initialCommit = repository.save(customer, metadata);

        assertThat(initialerCommit).isPresent();

        customer.addDeliveryAddressToAddressBookList(new DeliveryAddress("name", "surname", "street", "city"));

        Optional<CommitId> commitWithChanges = repository.save(customer, metadata);
        Optional<CommitId> commitWithoutChanges = repository.save(customer, metadata);

        assertThat(commitWithChanges).isPresent();
        assertThat(commitWithoutChanges).isPresent();

        ChangesByCommit initialChanges = repository.getCommitForCustomer(new CustomerId(customer.getId()), initialCommit.get());
        ChangesByCommit addedAddressBookChanges = repository.getCommitForCustomer(new CustomerId(customer.getId()), commitWithChanges.get());
        assertThrows(CommitNotFoundException.class, () ->  repository.getCommitForCustomer(new CustomerId(customer.getId()), commitWithoutChanges.get()));
    }

Solution

  • This is how Javers works, you can have empty commit (without snapshots), but you can't have no commit. This design is expressed in Javers API, when you call javers.commit() you get a Commit object and not Optional<Commit>.