Skip to content

Audit / Change History

If a ViewModel implements Auditable, the framework automatically adds a “History” button to the page toolbar. Clicking it opens a modal with a paginated, filterable listing of changes supplied by the history() method.


public interface Auditable {
ListingData<AuditEntry> history(
String searchText,
AuditFilters filters,
Pageable pageable,
HttpRequest httpRequest);
}

The signature mirrors ListingBackend.search(): the framework calls history() when the user searches or pages through the modal listing, passing the current search text, filters, and pagination state.


The filter form shown above the history listing. All fields are optional.

public record AuditFilters(
LocalDate dateFrom,
LocalDate dateTo,
String action,
String field,
String value
) {}
FieldDescription
dateFromShow entries on or after this date
dateToShow entries on or before this date
actionFilter by action label (e.g. "APPROVE", "DELETE", "SEND")
fieldFilter by the name of the changed field
valueFilter by old or new value

The row type rendered in the history listing.

public record AuditEntry(
LocalDateTime when,
String who,
String action,
String field,
String oldValue,
String newValue
) {}
FieldDescription
whenTimestamp of the change
whoUser or system actor that made the change
actionFree-form action label — can be "UPDATE", "APPROVE", "ARCHIVE", or any domain-specific string
fieldName of the changed field (empty for record-level actions)
oldValueValue before the change
newValueValue after the change

@UI("/customers/:id")
@Service
@Scope("prototype")
public class CustomerForm implements Auditable {
String id;
String name;
String email;
final AuditService auditService;
@Override
public ListingData<AuditEntry> history(
String searchText,
AuditFilters filters,
Pageable pageable,
HttpRequest httpRequest) {
return auditService.findByEntity("Customer", id, searchText, filters, pageable);
}
@Toolbar
Object save() { /* persist */ }
}

auditService.findByEntity(...) is your own service — Mateu only defines the contract. The service queries whatever audit store you use (a dedicated audit table, an event store, an external system, etc.) and maps the results to AuditEntry.


Because action is a plain String, you can use any labels that fit your domain:

Action stringWhen to use
CREATERecord first created
UPDATEOne or more fields changed
DELETERecord deleted or soft-deleted
APPROVE / REJECTWorkflow state transitions
SEND / EXPORTOperations with external side effects
LOGIN / LOGOUTSession-level events (for user audit)

There is no enforced vocabulary — use what makes sense for each entity.