Skip to content

React to Model transitions with Graph history

Trigger an action when a value crosses a threshold, rather than every time it is reported.

A room reports its temperature. Publish RoomBecameCold only when it goes from at least 18 degrees to below 18.

@Model
public record Room(@EntityId RoomId roomId, double temperature) {}
public final class RoomId extends Id<Room> {
public RoomId(String id) { super(id); }
}
public record ReportTemperature(RoomId roomId, double temperature) {
@Apply
Room apply(@Nullable Room room) { return new Room(roomId, temperature); }
}
public record RoomBecameCold(RoomId roomId) {}
public class ColdRoomAlert {
@HandleEvent
void handle(ReportTemperature event, Graph<Room> room) {
var previous = room.previous();
if (previous != null && !previous.isEmpty()
&& previous.get().temperature() >= 18 && room.get().temperature() < 18) {
Fluxzero.publishEvent(new RoomBecameCold(event.roomId()));
}
}
}

The injected Graph belongs to this event. It compares the states around that observation even if newer readings have already arrived. The first reading establishes a baseline and does not trigger an alert.

var roomId = new RoomId("living-room");
TestFixture.create(new ColdRoomAlert())
.givenCommands(new ReportTemperature(roomId, 20))
.whenCommand(new ReportTemperature(roomId, 17))
.expectEvents(new RoomBecameCold(roomId))
.andThen()
.whenCommand(new ReportTemperature(roomId, 16))
.expectNoEventsLike(RoomBecameCold.class);

This avoids alerts for consecutive low readings. Re-delivery of the crossing event can still repeat the reaction; any external action should follow the API integration guide. See Temporal Graphs for historical navigation.


© 2026 Fluxzero