MVVM Conventions for UE5.7
This document describes ViewModel patterns and conventions used in our Unreal Engine 5.7 project with the MVVM plugin.
A ViewModel represents a domain entity — a meaningful concept from the game's domain model (Item, Character Health, Inventory Cell, Drag Operation) — expressed as a set of bindable properties for UI.
Key principles
VM = Entity, not Widget. A ViewModel describes a domain object, not a specific UI element. One VM instance can be bound to multiple widgets simultaneously through Resolvers.
VM Composition. ViewModels can contain other ViewModels as nested sub-entities. Example:
UXtrAttributesViewModelcontainsPrimaryVM,VitalityVM,CombatVM— each representing a sub-entity of the character's attribute system.VM Ownership. A ViewModel is owned by its Model (Component, Subsystem, or parent VM). The Model populates data through protected setters; UI only reads data and sends Requests.
VM Sharing. Multiple widgets can bind to the same VM instance. A Resolver returns the VM from the owning Subsystem/Component to any widget that requests it.
One entity — one VM. If a domain concept has its own identity and can appear in multiple UI contexts, it deserves its own ViewModel class. Do not duplicate entity fields across unrelated ViewModels — compose them instead.
Overview
ViewModel is a contract between Model (Component/Subsystem) and UI (Widget). Model updates data, UI reads it and sends Requests.
Property Types
Data
BlueprintReadOnly
Yes
No
Data for UI display
Request
BlueprintReadWrite
Yes
Yes
Signals from UI to Model
Computed
— (UFUNCTION only)
Yes
—
Derived values
Broadcast-only
— (UFUNCTION only)
Yes
—
UI trigger without value
Boolean Getter Naming
Is*
Object state (what is it?)
IsEmpty(), IsDragging(), IsAlive()
Has*
Presence of something (does it have?)
HasTarget(), HasNotifications()
Can*
Ability to perform action (can it?)
CanInteract(), CanDrop()
Request fields always use Has*Request(): HasDropRequest(), HasCloseRequest().
Data Property
Data for display. UI only reads, Model only writes.
Rules:
BlueprintReadOnly— UI cannot writeGetterwithoutSetterin UPROPERTY — setter is not exported to BlueprintSetter in
protectedsection — accessible only to friend class (Model)meta=(AllowPrivateAccess=true)— allows access to private field
Request Property
Signal from UI to Model. UI writes, Model reads and resets.
Rules:
BlueprintReadWrite— UI can writeBoth
GetterandSetterin UPROPERTY — both are exportedSetter is
BlueprintCallablein public sectionCategory contains
|Requestsuffix for clarity
Sentinel values for Request:
bool:falseint32:INDEX_NONE(-1)FVector2D: constantNoRequest=FVector2D(-1, -1)
Request Pattern (UI to Model)
UI never calls Model methods directly. Instead, the Request-field pattern is used:
ViewModel contains a Request-field with a sentinel value
UI (Blueprint) changes the Request-field value when user performs an action
Model (Component/Subsystem) subscribes to field changes via
AddFieldValueChangedDelegateModel upon notification:
Checks that value != sentinel (ignores reset)
Resets field back to sentinel immediately
Validates the request
Executes logic
Important:
Request-field is a one-shot signal, not state
Model ignores sentinel to avoid infinite loop
Subscription via
CreateUObjectcreates weak reference — explicit unsubscription not needed with shared lifetime
Computed Property
Derived value calculated from other properties.
Rules:
UFUNCTION only, no UPROPERTY
FieldNotifyon function — UI can subscribe to changesModel calls
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHealthPercent)when dependent data changes
Cascade broadcasting — when one property affects multiple computed properties:
Broadcast-only Property
Trigger for UI when "something changed". Value doesn't matter, only the broadcast itself.
When to use:
UI needs to know "something updated" without checking each field
Optimization: single binding instead of many
Friend Class Pattern
ViewModel declares Model as friend for access to protected setters:
Complete ViewModel Example
Summary
UPROPERTY
Yes
Yes
No
UFUNCTION getter
Yes (public)
Yes (public)
Yes (public, FieldNotify)
Setter location
protected
public
N/A
BlueprintReadOnly/Write
ReadOnly
ReadWrite
N/A
Setter in UPROPERTY
No
Yes
N/A
Who writes
Model only
UI only
N/A (derived)
Who reads
UI
Model
UI
Last updated