Learn
Model and ModelBinding
Assemble one immutable feature snapshot from one or several business boundaries without exposing infrastructure to View.
Model and ModelBinding
Ark MVP deliberately has no universal Model base class. Each feature defines an ordinary immutable type that represents its complete current input.
final class ProfileModel {
const ProfileModel({
required this.profile,
required this.saveState,
required this.reload,
required this.save,
});
final ProfileState profile;
final SaveProfileState saveState;
final void Function() reload;
final void Function(String name) save;
}
Model is not a domain entity and not a second state manager. It is a presentation-feature snapshot assembled from business boundaries the application already owns.
Binding state to the runtime
final binding = ModelBinding<ProfileModel>(
read: () => ProfileModel(
profile: profileManager.state,
saveState: saveManager.state,
reload: profileManager.reload,
save: saveManager.save,
),
changes: <Stream<Object?>>[
profileManager.changes,
saveManager.changes,
],
);
Values emitted by changes are signals, not partial updates. After any signal, the host calls read() and obtains a new complete Model. Presenter therefore never needs to identify the emitting source or maintain a second merge algorithm.
Snapshot requirements
read()is synchronous;- every returned Model must remain stable after it is returned;
- every read must describe the complete feature state;
- change streams remain owned by the application;
- cross-manager atomicity is not invented by
ModelBinding.
When several business values must change atomically, compose them behind one business-state boundary before exposing them to MVP.
Callbacks or an action interface
Small Models can carry specific callbacks. Larger features may define a typed action interface. Both forms are valid when they expose permitted feature operations rather than general infrastructure access.