Skip to content

Relationships vs embedded CRUDs

Mateu intentionally distinguishes between:

  • lightweight relationships rendered through @Lookup
  • embedded child CRUDs rendered through orchestrators

Use @Lookup when:

  • the field stores ids or scalar references
  • the UI only needs selection + label resolution
  • the relationship should stay decoupled from the domain model

Example:

@Lookup(search = RoleOptionsSupplier.class, label = RoleLabelSupplier.class)
List<String> roles;

Mateu will automatically generate:

  • selection UI (checkbox, dropdown, etc.)
  • label resolution
  • integration with forms and CRUD

If you declare:

List<Role> roles;

Mateu will infer an editable structure (like a table).

This is NOT treated as a relationship with repository-backed lookup.

This is intentional.

Mateu avoids leaking domain behavior into the UI layer.


If the child collection has its own lifecycle, use an embedded CRUD.

Example:

Callable<?> steps = () -> MateuBeanProvider.getBean(Steps.class).withProcessId(id);

Where:

public class Steps extends AutoListOrchestrator<Step> { ... }

This gives you:

  • full CRUD behavior
  • independent lifecycle
  • master-detail UI

flowchart TD
Q{What do you need?}
Q -->|Select & display a related entity| L["@Lookup\nreference — lightweight, decoupled"]
Q -->|Editable embedded structure| S["List&lt;Entity&gt;\nstructure — not a relationship"]
Q -->|Full CRUD for child entities| E["Embedded orchestrator\nCallable&lt;?&gt; → AutoListOrchestrator"]
  • @Lookup → reference (lightweight, decoupled)
  • List<Entity> → structure (not a relationship)
  • embedded orchestrator → real child aggregate UI

This prevents:

  • accidental coupling between UI and domain
  • implicit ORM-like behavior
  • hidden data loading

Instead, everything is explicit and composable.