Back

Product code

Product decisions
are the code.

When the cloud handles the engineering underneath, a feature can be just its business rules and follow-up actions. These examples show what that leaves your agent to write.

You build in natural language. Fluxzero chose Java for the code underneath: a proven backend language that agents speak fluently. Your code remains easy to extend as your product grows.

01 · Making changes

Only product logic.
Working at any scale.

You decide how a feature should work. Fluxzero keeps its rules intact when many customers act at once. The same logic continues to work as usage grows, without engineering work for your agent.

ReserveTicket.java One rule, two changes
record ReserveTicket(
    @NotNull ReservationId reservationId,
    @NotNull TicketId ticketId) {

    @AssertLegal
    void assertAvailable(Ticket ticket) {
        if (ticket.status() != AVAILABLE) {
            throw TicketErrors.notAvailable;
        }
    }

    @Apply
    Ticket reserve(Ticket ticket) {
        return ticket.withStatus(RESERVED);
    }

    @Apply
    Reservation createReservation(User user) {
        return Reservation.awaitingPayment(
            reservationId, ticketId, user.id());
    }
}
State loads automatically
Fluxzero provides the product state and which customer is making the change.
Everything changes together
If something changes while the rule runs, Fluxzero runs it again before saving.
Many people, one safe result
Each decision is kept correct even when many customers act at once.

02 · Keeping track

Your product changes.
Its history stays.

Define the parts of your product and how they fit together. Fluxzero keeps the information up to date and preserves its history. Your app can use that history directly in its features.

Show.java Root model
@Model
record Show(
    @EntityId ShowId showId,
    String name,
    ShowStatus status) {}
Ticket.java Child of Show
@Model
record Ticket(
    @EntityId TicketId ticketId,
    @Parent(pathInParent = "tickets")
    ShowId showId,
    TicketDetails details,
    @With TicketStatus status) {}
Reservation.java Child of Ticket
@Model
record Reservation(
    @EntityId ReservationId reservationId,
    @Parent(pathInParent = "reservations")
    TicketId ticketId,
    String customerId,
    @With ReservationStatus status,
    @With @ProtectData String contactEmail) {}
Keep track of everything
Fluxzero keeps track of every part of the product and how it relates to the rest.
Stored automatically
Fluxzero loads and saves the information without storage work in the application.
History preserved
Fluxzero keeps every previous state of the product available.

03 · Testing behavior

Know it works.
Before customers use it.

Your agent tests that features keep working as you build. That includes the edge cases. Fluxzero runs the same product code and rules in tests as in production, so you can ship with confidence.

ReserveTicketTest.java Behavior test with JSON
@Test
void reservesAnAvailableTicket() {
    fixture
        .whenCommandByUser("customer-42",
            "/ticketing/reserve-ticket.json")
        .expectEvents(
            "/ticketing/reserve-ticket.json");
}

@Test
void rejectsASecondReservation() {
    fixture
        .givenCommandsByUser("customer-42",
            "/ticketing/reserve-ticket.json")
        .whenCommandByUser("customer-77",
            "/ticketing/reserve-ticket.json")
        .expectExceptionalResult(TicketErrors.notAvailable)
        .expectNoEvents();
}
Production behavior
Fluxzero runs the scenario through the same rules as the real application.
Real circumstances
Fluxzero supplies the person and moment described by the test.
Visible outcome
Fluxzero confirms success or the expected rejection. No infrastructure or mocks needed.

05 · Set a schedule

Your product follows up.
Even after a restart.

Scheduling is part of almost every application. Fluxzero makes it easy to schedule and test what should happen later. Scheduled work survives restarts and deployments, with your rules checked when it runs.

ReservationTimers.java Start and cancel a deadline
@Component
class ReservationTimers {
    @HandleEvent
    void start(ReserveTicket event) {
        Fluxzero.scheduleCommand(
            new ExpireReservation(event.reservationId()),
            ScheduleId.of("expire", event.reservationId()),
            Duration.ofMinutes(15));
    }

    @HandleEvent
    void stop(ConfirmReservation event) {
        Fluxzero.cancelSchedule(
            ScheduleId.of("expire", event.reservationId()));
    }
}
ExpireReservation.java Remove hold and release ticket
record ExpireReservation(@NotNull ReservationId reservationId) {

    @AssertLegal
    void assertAwaitingPayment(Reservation reservation) {
        if (reservation.status() != AWAITING_PAYMENT) {
            throw ReservationErrors.notAwaitingPayment;
        }
    }

    @Apply
    Reservation remove(Reservation reservation) {
        return null;
    }

    @Apply
    Ticket release(Ticket ticket) {
        return ticket.withStatus(AVAILABLE);
    }
}
Scheduled work preserved
Fluxzero retains the action through application restarts and deployments.
Current rules checked
Fluxzero loads the latest data and checks whether the scheduled change is still allowed.
Cancellation handled
Your code names the follow-up to cancel. Fluxzero removes it from the schedule.

06 · Run a workflow

Workflows pick up
where they left off.

Some workflows need to wait for an external service before continuing. Define what happens when the response arrives. Fluxzero preserves progress and connects each response to the right process, even after a restart.

PaymentProcess.java Wait for a payment response
@Stateful
record PaymentProcess(
    @EntityId ReservationId reservationId,
    @Association String pspReference) {

    @HandleEvent
    static PaymentProcess start(PaymentStarted event) {
        return new PaymentProcess(
            event.reservationId(), event.pspReference());
    }

    @HandleEvent
    PaymentProcess complete(PaymentSucceeded event) {
        Fluxzero.sendCommandAndWait(
            new ConfirmReservation(reservationId));
        return null;
    }
}
Progress preserved
Fluxzero remembers where each workflow left off, including after a restart.
Responses connected
The reference on an incoming response connects it to the right waiting process.
Ready to continue
Fluxzero loads that process and runs the next step with its saved state.

07 · Access and privacy

You decide who may do what.
Fluxzero enforces it.

Define the access rules that fit your product. Fluxzero enforces them before allowing a change. Information marked as private stays protected and can be erased without losing the surrounding history.

UpdateContactEmail.java Ownership and protected data
@RequiresUser
record UpdateContactEmail(
    @NotNull ReservationId reservationId,
    @NotBlank @Email @ProtectData
    String contactEmail) {

    @AssertLegal
    void assertOwner(Reservation reservation, User user) {
        if (!reservation.customerId()
                .equals(user.id())) {
            throw ReservationErrors.notOwner;
        }
    }

    @Apply
    Reservation update(Reservation reservation) {
        return reservation.withContactEmail(contactEmail);
    }
}
Identity supplied
Fluxzero gives your rule the authenticated customer making the change.
Access checked first
With sign-in required, Fluxzero rejects unauthenticated requests before the feature runs.
Private data stays erasable
Fluxzero stores protected values separately, so they can be erased while the surrounding history remains.

08 · Reach in through the web

Public web endpoints.
Private applications.

Your web endpoints live in Fluxzero. Your application has no public endpoint of its own and securely pulls requests when it can handle them. This keeps it out of reach of direct network attacks. Excess traffic waits in Fluxzero instead of flooding the application.

ReservationApi.java Web request to command
@Component
@Path("/api/reservations")
class ReservationApi {
    @HandlePost
    ReservationId reserve(@Valid ReservationRequest request) {
        var reservationId = Fluxzero.generateId(
            ReservationId.class);

        Fluxzero.sendCommandAndWait(
            new ReserveTicket(
                reservationId, request.ticketId()));
        return reservationId;
    }
}
Input checked
Fluxzero checks the submitted data against your requirements before running the feature.
Identity carried through
The feature receives the customer making the request without extra code to pass that identity along.
Response handled
Fluxzero sends the result back to the interface and turns errors into the appropriate web response.

09 · Connect your features

The same code.
Wherever the work runs.

Send a request without choosing which application will handle it. Fluxzero delivers it and returns the result. Whether the handler runs in your own app or another one, the calling code stays the same.

TicketApi.java Requests without routing code
@Component
@Path("/api/tickets")
class TicketApi {
    @HandleGet
    List<Ticket> available(
            @QueryParam ShowId showId,
            @QueryParam String section) {
        return Fluxzero.queryAndWait(
            new FindAvailableTickets(showId, section));
    }
}
Web handlers anywhere
The web handler can run in any connected application. The public endpoint stays the same.
Queries across applications
The query handler can run alongside it or in another app. The call stays the same.
One request, more uses
Other apps can observe the same web request for analytics while the endpoint handler returns the response.

10 · Scale independently

Each part can scale
independently.

Not every part of your application is equally busy. With separate consumers, parts run and scale independently. Fluxzero keeps their progress separate and distributes the work, so capacity can grow where it is needed.

FraudDetection.java Fraud checks with their own capacity
@Component
@Consumer(name = "fraud-detection", threads = 4)
class FraudDetection {
    @HandleEvent
    void check(ConfirmReservation event) {
        FraudAssessment assessment = Fluxzero.queryAndWait(
            new CheckForFraud(event.reservationId()));

        if (assessment.requiresReview()) {
            Fluxzero.sendAndForgetCommand(
                new ReviewReservation(
                    event.reservationId(), assessment));
        }
    }
}
Capacity where it counts
Give demanding work its own consumer. Add threads or app instances as demand grows.
Independent progress
Each consumer keeps its own place. Slower work does not hold up other consumers.
Work shared automatically
Fluxzero distributes the work across instances of the same consumer.

11 · Using the past

New features can be built
from historical data.

Fluxzero preserves your product’s history. Use it to add features retroactively, with the information as it was at the time. The same code processes earlier activity and continues with new activity.

CustomerRewards.java Retroactive event handler
@Component
@Consumer(name = "customer-rewards", minIndex = 0)
class CustomerRewards {
    @HandleEvent
    void reward(
            ConfirmReservation event,
            Reservation reservation) {
        Fluxzero.assertAndApply(new GrantReward(
            event.reservationId(), reservation.customerId(), 100));
    }
}
History supplied
Fluxzero feeds recorded activity to the new feature from the point you choose.
Earlier state reconstructed
Fluxzero supplies the data as it was then, without your code having to rebuild it.
Progress remembered
Fluxzero tracks how far the feature has processed and resumes after interruptions.

The result

Your product is
the entire application.

Your agent writes what the product should do. Fluxzero takes responsibility for making it run. What remains is an application entirely devoted to the product, from the first feature to a fully distributed system.