Learn

Model and ModelBinding

Assemble one immutable feature snapshot from one or several business boundaries without exposing infrastructure to View.

v1.1.0Beginner

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.

dart
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

dart
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.