ViewModels in Vaadin?

Hello all,

Now when we have signals, I’ve been thinking about how to adapt the ViewModel pattern from Android to Vaadin.

In Android, the pattern serves many purposes that don’t apply to Vaadin. The idea that we could use in Vaadin as well is that the ViewModel contains all the state and all the actions of the UI. The UI (a view, a dialog, a component) then only binds to the ViewModel. In the best case, your UI implementation ends up being a single class with a single constructor that takes the ViewModel as its only parameter.

This forum post is me thinking out loud and asking for input.

UI State

The ViewModel would contain all the state of the UI in the form of signals. You could either have the class expose the signals directly as public methods like this:

public class MessageEditorVM implements Serializable {
  private final ValueSignal<String> subject = new ValueSignal<>("");
  private final ValueSignal<String> body = new ValueSignal<>("");

  public Signal<String> subject() {
    return subject;
  }

  public Signal<String> body() {
    return body;
  }
}

You could also put all the signals inside a separate State record. This makes it easier to see exactly which UI state a particular VM provides:

public class MessageEditorVM implements Serializable {
  public record State(Signal<String> subject, Signal<String> body) {}

  private final ValueSignal<String> subject = new ValueSignal<>("");
  private final ValueSignal<String> body = new ValueSignal<>("");
  private final State state = new State(subject, body);

  public State state() {
    return state;
  }
}

Actions

Every action a user can perform should be represented in the ViewModel. This includes editing a single form field. You could expose the actions as public methods on the ViewModel:

public class MessageEditorVM implements Serializable {
  private final ValueSignal<String> subject = new ValueSignal<>("");
  private final ValueSignal<String> body = new ValueSignal<>("");

  public Signal<String> subject() {
    return subject;
  }

  public Signal<String> body() {
    return body;
  }

  public void setSubject(String subject) {
    this.subject.set(subject);
  }

  public void setBody(String body) {
    this.body.set(body);
  }

  public void send() {
    // TODO Call a backend service to send the message
  }
}

In the UI, you could then bind the ViewModel with something like this:

subjectField.bindValue(messageEditorVM.subject(), messageEditorVM::setSubject);
bodyField.bindValue(messageEditorVM.body(), messageEditorVM::setBody);
sendBtn.addClickListener(_ -> messageEditorVM.send());

If you wanted to make it more explicit which actions are actually available in the VM, you could also use a sealed interface and records like this:

public class MessageEditorVM implements Serializable {
  public record State(Signal<String> subject, Signal<String> body) {}

  public sealed interface Action {
    public record SetSubject(String subject) implements Action {}
    public record SetBody(String body) implements Action {}
    public record Send() implements Action {}
  }

  private final ValueSignal<String> subject = new ValueSignal<>("");
  private final ValueSignal<String> body = new ValueSignal<>("");
  private final State state = new State(subject, body);

  public State state() {
    return state;
  }

  public void onAction(Action action) {
    switch (action) {
      case Action.SetSubject(String subject) -> this.subject.set(subject);
      case Action.SetBody(String body) -> this.body.set(body);
      case Action.Send _ -> { /* Send the message */ }
    }
  }
}

This is a bit more verbose to write and especially calls to bindValue become more complicated. I’m also not sure whether it makes the ViewModel code easier to read and understand. I kind of like that you can see which actions are available by looking at the Action interface, and that there is a single entrypoint for actions – the onAction method.

In real applications, you typically want to nest ViewModels inside each other. For instance, a MessageViewVM might contain multiple instances of MessageEditorVM. This makes things more complicated but also provides more implementation alternatives, each with their own pros and cons. I will return to this topic in a forum post of its own.

What are your thoughts now after reading this post? Do we need something like the ViewModel pattern in Vaadin? Or does it only introduce unnecessary complexity?

-Petter-

Hi Petter

I think the pattern makes sense for complex views with lots of state. For simple forms, it is over-engineering. The name “Presentation Model” or simply “StateHolder” would be more accurate than “ViewModel” as it’s not a ViewModel with the actions and signals

Maybe a minor concern, but my immediate thought reading this was, “isn’t there a single abstraction that could be used here instead of needing to specify both foo() and ::setFoo?”

That bindValue is already existing API. The biggest advantage of separating reading and writing is that you can trigger additional logic in the writing and also decide not to write it. That way you don’t have to deal with things like “vetoable change listener” from the early JavaBeans.

If we follow Android’s definition of view model, then yes, Presentation Model would be a better fit here (but if you look at the wikipedia page for MVVM, it says the pattern is a variation of the presentation model). State Holder to me seems like it should only contain state, not actions to manipulate the state.

And I fully agree this would be over-engineering for simple forms.

I think the idea is fundamentally a good one and would welcome it if the Vaadin framework already provided some of the basics and prerequisites for using a view model as an integral part of Vaadin views.

Of course, for simple views and forms, that’s too much, but beyond that, in real-world business applications, I certainly see potential for its use:

I already try to use a dedicated view model in some of my views in order to encapsulate UI from logic. And since the increased use of AI coding agents and the growing importance of testing (particularly UI unit tests), I have found another area of application (or use case if you want :wink:) for such view models: as already mentioned in the Wikipedia article on MVVM, the view model can also

serve to encapsulate access to the backend using use cases.

These use cases are now becoming increasingly important, as they are an integral part of Spec-Driven AI Development. Maybe we could derive the view model directly from the Spec with the set of use cases and vice versa … some day. In the past, DSLs and model driving approaches were used for such tasks.

I would also like to point out that the Vaadin documentation for end-to-end tests mentions the Page Object Pattern. I saw this as a conceptual precursor to view models, even though they naturally serve a different purpose.

To put it in a sort of fractal or self-similar way :see_no_evil::

A good use case for the availability of a view models
would be the availability of the view’s use cases

Don’t mix use cases and ViewModels @todo42

Use cases are driven by actors means the use cases include the UI.

Speaking of AI driven development AI doesn’t need such abstractions.

How does the ViewModel change when the list of actions depends on the user?

Could a ViewModel be used for 2 different Entities/database tables that have the same view?