Skip to content

Build Your Domain Vocabulary

Mateu’s annotations and interfaces are deliberately generic — @Lookup, @Stereotype, ComponentTreeSupplier, StateSupplier… They describe UI mechanics, not your domain.

On a real codebase you repeat the same mechanics over and over: every supplier id is the same @Lookup, every amount is the same @Stereotype(money) @Label @Help, every legacy DTO needs the same wiring. The cure is to build a thin semantic layer on top of Mateu — a small vocabulary that names the concepts of your domain and hides the mechanics behind them.

This page frames the three mechanisms that let you do that and when to reach for each. Each links to its reference page for the full detail.

You want to…UseReference
Re-label / bundle existing Mateu configuration under a domain nameSemantic (composed) annotationSemantic Annotations
Render an object that is not a Mateu component (a plain/legacy model) and round-trip itComponentAdapterComponentAdapter
Take full control of the component tree, the state, or the option listsCustom suppliersComponentTreeSupplier, StateSupplier, DataSupplier

The three compose: an adapter can return components that use your semantic annotations; a supplier can back a field that a semantic annotation declares. Start with the lightest one that fits.

1. Semantic annotations — name your configuration

Section titled “1. Semantic annotations — name your configuration”

When the same combination of Mateu annotations appears again and again, fold it into one domain annotation. A supplier-id field is normally:

@Lookup(search = ProveedorOptionsSupplier.class, label = ProveedorLabelSupplier.class)
String proveedorId;

Define the concept once and reuse the meaningful name:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Lookup(search = ProveedorOptionsSupplier.class, label = ProveedorLabelSupplier.class)
public @interface ProveedorId {}
// everywhere:
@ProveedorId String proveedorId;

One annotation can bundle several (@Stereotype(money) @Label @Help), and it works on fields, methods and classes. This is the lightest option — no new code paths, just a name. → Semantic Annotations

2. Component adapters — adopt foreign models

Section titled “2. Component adapters — adopt foreign models”

A semantic annotation still assumes the object is a Mateu form. When the object is a plain domain/legacy model with no Mateu annotations — or one you can’t or don’t want to annotate — register a ComponentAdapter<T> that defines its whole UI and rebuilds it from state:

@Service
public class PedidoAdapter implements ComponentAdapter<Pedido> {
public Class<Pedido> type() { return Pedido.class; }
public AdaptedView adapt(Pedido p, HttpRequest r) { /* components + state + actions */ }
public Pedido deserialize(Map<String, Object> state, HttpRequest r) { /* rebuild */ }
}

The model renders both as a top-level route and as a nested field (an independent island with its own state and actions). → ComponentAdapter

3. Custom suppliers — control the mechanics

Section titled “3. Custom suppliers — control the mechanics”

When even an adapter is too coarse and you need to shape the raw output, implement the supplier interfaces directly: a ComponentTreeSupplier to emit an arbitrary component tree for a route, a StateSupplier to decide exactly what state is sent to the client, a DataSupplier/options supplier to feed lookups and grids. These are the building blocks the two mechanisms above are built on, exposed for when you need them. → Interfaces reference

Routing annotations (@UI, @Route, @Routes, @HomeRoute) are not part of the semantic layer — they are resolved by the annotation processor at compile time, which is not meta-annotation aware. Keep them directly on your classes; everything else above is runtime-resolved and composable.