Skip to content

Audience projection

Status: ✅ Implemented

The same screen often serves several personas: the front-desk agent needs the internal notes and the folio balance; the guest doing self check-in should see a welcome message and nothing operational. Duplicating the screen per persona forks the model and the two copies drift. Declare one model and mark which elements belong to which audience; a header switch projects the model per persona.

Two pieces:

  1. The switch — an @AppContext field named exactly audience on the app class. It renders as a selector on the app header and its value travels with every request. An enum gives you the options for free (constants travel as their name()):
@UI("")
public class BackofficeApp {
enum Modo { Staff, Cliente }
@AppContext(label = "Modo")
Modo audience;
// ... @Menu entries ...
}
  1. The marks@Audience({"..."}) on any form field, @Button/@Toolbar action method or @Menu entry. The values are matched (case-sensitively) against the current audience app-context value:
@UI("/checkin")
public class CheckIn {
String bookingCode; // every audience
@Audience("Cliente")
@PlainText @Multiline
String welcome = "¡Bienvenido!"; // only in the guest projection
@Audience("Staff")
@Multiline
String internalNotes; // only in the staff projection
@Audience("Staff")
@Stereotype(FieldStereotype.money)
BigDecimal folioBalance;
@Audience("Staff")
@Toolbar
public Message audit() { ... } // the button disappears for guests
}

An element may declare several audiences: @Audience({"Staff", "Manager"}).

When no audience is selected (the selector is empty), everything is visible: no projection is active, so the developer — or a supervisor — sees the full model. Selecting an audience activates the projection: elements marked for another audience disappear; unmarked elements always stay.

Current audienceUnmarked element@Audience("Staff") element
(unset)shownshown
Staffshownshown
Clienteshownhidden
  • Form fields (including inline subforms and listing columns that go through the standard field filter).
  • Action methods@Button and @Toolbar buttons are dropped from the page when their audience doesn’t match.
  • Menu entries@Menu fields and methods of the app class.

The annotation is composable (semantic annotations can embed it), and the same behaviour ships in the C# ([Audience("staff")]) and Python (Audience("staff") marker / @audience(...) method decorator) backends.

Use it whenever one screen serves several personas whose relevance differs but whose rights don’t have to (kiosk vs desk mode, agent vs customer view, novice vs expert density). Pair it with @AppContext for the switch and with the security annotations when visibility must actually be enforced. Demo: /audience-demo (switch the “Modo” selector between Staff and Cliente).