Core concepts
Fluxzero lets you build application behavior around messages. A command asks for a change, a query asks for information, and an event tells other parts of the application what happened. Your Java or Kotlin code handles those messages; Fluxzero takes care of their delivery and the storage of your Models.
Let’s build one small behavior: remember a task and mark it complete. The examples below belong together.
Java uses Lombok’s @With for immutable copies; Kotlin uses copy. Put public Java types in separate files.
Models
Section titled “Models”A Model is something your application remembers and changes over time. A task needs an identity, a name and a completion status:
@Modelpublic record Task(@EntityId TaskId taskId, String name, @With boolean completed) {}
public final class TaskId extends Id<Task> { public TaskId(String id) { super(id); }}@Modeldata class Task(@EntityId val taskId: TaskId, val name: String, val completed: Boolean)
class TaskId(id: String) : Id<Task>(id)@EntityId identifies the task. Id<Task> tells Fluxzero which Model that ID belongs to. We can now address one task
without loading every task in the application.
Commands
Section titled “Commands”A command names an action. Its @Apply method returns the Model state that should result:
public record CreateTask(TaskId taskId, String name) { @Apply Task apply() { return new Task(taskId, name, false); }}
public record CompleteTask(TaskId taskId) { @Apply Task apply(Task task) { return task.withCompleted(true); }}data class CreateTask(val taskId: TaskId, val name: String) { @Apply fun apply() = Task(taskId, name, false)}
data class CompleteTask(val taskId: TaskId) { @Apply fun apply(task: Task) = task.copy(completed = true)}Send the action through Fluxzero:
var taskId = new TaskId("t1");Fluxzero.sendCommandAndWait(new CreateTask(taskId, "Read the guide"));Fluxzero.sendCommandAndWait(new CompleteTask(taskId));val taskId = TaskId("t1")Fluxzero.sendCommandAndWait<Any?>(CreateTask(taskId, "Read the guide"))Fluxzero.sendCommandAndWait<Any?>(CompleteTask(taskId))Fluxzero finds the task from the typed ID, applies the change and stores it. There is no forwarding command handler
to write. Register these command types in your application; Spring discovers them within its configured scan.
TestFixture, used below, discovers these payload-side applies automatically.
Rules and history
Section titled “Rules and history”In a real task app, you can require a nonblank name with @NotBlank and check who may complete a task with
@AssertLegal. These checks happen before the change is stored. See
validation and Model updates.
By default, a Model keeps its accepted changes as events and reconstructs its state from them. This is event sourcing. You can choose other storage behavior later without changing what a command means to your users.
Queries
Section titled “Queries”A query asks for information. Its handler returns the answer, and Request<Task> declares the expected result type:
public record GetTask(TaskId taskId) implements Request<Task> { @HandleQuery Task handle() { return Fluxzero.loadModel(taskId).get(); }}
Task task = Fluxzero.queryAndWait(new GetTask(new TaskId("t1")));data class GetTask(val taskId: TaskId) : Request<Task> { @HandleQuery fun handle(): Task? = Fluxzero.loadModel(taskId).get()}
val task = Fluxzero.queryAndWait(GetTask(TaskId("t1")))This query loads a known task. To find tasks by their contents, enable search and use the Model query APIs.
Handlers
Section titled “Handlers”Handlers are methods that respond to messages. The annotation describes which kind:
| Annotation | Responds to |
|---|---|
@HandleCommand | An action that needs explicit handling, such as calling an external service |
@HandleQuery | A request for information |
@HandleEvent | Something that has happened |
@HandleGet, @HandlePost | An HTTP request |
@HandleSchedule | A scheduled message becoming due |
The GetTask handler lives on its payload and runs locally by default. Separate handler objects are useful for
reacting to several message types. Register those objects with your application, or let Spring discover
@Component classes. See configuration.
Events
Section titled “Events”After an accepted Model change, Fluxzero normally publishes the applied command payload as an event. CompleteTask
as a command asks to complete the task; the same payload as an event tells other handlers that the change happened.
For example, an activity logger can react to completion without being part of the command:
@HandleEventvoid on(CompleteTask event, Task task) { log.info("Completed task: {}", task.name());}@HandleEventfun on(event: CompleteTask, task: Task) { log.info("Completed task: {}", task.name)}Here log is your application’s logger. Fluxzero injects the task’s state at that event. Other consumers can react
independently, for example by sending a notification.
HTTP and scheduled work
Section titled “HTTP and scheduled work”An HTTP endpoint is a handler too:
@HandleGet("/hello")String hello() { return "Hello, world!"; }@HandleGet("/hello")fun hello() = "Hello, world!"An endpoint can send commands or queries to expose your app’s behavior.
Scheduled messages use the same idea: schedule a reminder for tomorrow, then handle it with @HandleSchedule when due.
TestFixture runs these behaviors locally, without a database or platform mocks. Create a task, complete it, and
check its state through the query:
var taskId = new TaskId("t1");TestFixture.create() .givenCommands(new CreateTask(taskId, "Read the guide"), new CompleteTask(taskId)) .whenQuery(new GetTask(taskId)) .expectResult(new Task(taskId, "Read the guide", true));val taskId = TaskId("t1")TestFixture.create() .givenCommands(CreateTask(taskId, "Read the guide"), CompleteTask(taskId)) .whenQuery(GetTask(taskId)) .expectResult(Task(taskId, "Read the guide", true))Tests can also check rejected actions, outgoing messages, HTTP responses and scheduled work.
Related Models
Section titled “Related Models”When tasks belong to projects, keep their independent identities and connect them with @Parent.
A Graph lets you navigate those relationships and read a project together with its tasks.
The to-do tutorial builds that relationship step by step, along with validation,
authorization, queries and reminders. The developer guides cover individual capabilities.
© 2026 Fluxzero