This is a brief technical explanation of MVVM, with enough detail (borrowed from its WPF implementation) and examples to allow the reader to grasp how it actually works.
- MVVM is an architectural design pattern for building interactive applications. Its aim is to achieve complete decoupling of application logic from presentation logic.
- MVVM is not something new, and it was not even new at the time that it became popular. It was named by John Gossman in 2005, who states that it is the same as the Presentation Model pattern named by Martin Fowler in 2004, who in turn states that it was previously known as the Application Model pattern in certain Smalltalk circles as early as in the 1980s, and that’s where we lose track of it: for all we know, it may have originated in Ancient Egypt.
- The acronym stands for Model, View, Viewmodel.
- Model refers to the main estate data model of our application; it is optional and largely irrelevant, so it will only be mentioned a couple of times here.
- View refers to the user interface.
- Viewmodel is the secret sauce, but in essence it refers to the application logic.
- Application logic is placed in objects known as viewmodels. A viewmodel does the following:
- Publicly exposes state, part of which is publicly mutable.
- Issues notifications about any mutation of its state.
- Responds to state mutations with behavior, such as querying and updating data stores, issuing events in some messaging backbone, etc.
- Manifests its behavior by means of further modifying its own state, which in turn generates more state mutation notifications.
- This allows the following very simple workflow:
- When the presentation layer modifies a property of a viewmodel, the viewmodel takes notice and exhibits its behavior.
- When a viewmodel modifies one of its own properties, the presentation layer takes notice and updates the screen.
- Thus, a viewmodel essentially implements a fully interactive and yet completely abstract (i.e. not graphical) user interface, with mutable properties instead of editable controls. The viewmodel is free from presentation concerns such as where on the screen the properties may be shown, what user interface controls may be used to show them, etc.
- Note: GUI pushbuttons, which have no state, are implemented as special “Command” objects that are exposed by a viewmodel besides its properties, but they could also be implemented as Boolean properties, where a transition from say, true to false triggers behavior. Command objects make viewmodels more self-documenting and more usable, but they are nothing but a nice-to-have feature: in principle, everything could work with just properties.
- If we wanted to allow the user to edit a
Customerentity in a modal dialog box:- There will be a viewmodel for this dialog box, which exposes:
- One property for each editable field of
Customer. - One command for the ‘OK’ button
- One boolean property which stands for the ’enabled’ state of the ‘OK’ button.
- One command for the ‘Cancel’ button, presumed to be always enabled.
- One property for each editable field of
- The viewmodel may set the enabled state of the ‘OK’ command to true only once the user has made changes to the fields.
- When the ‘OK’ command is triggered, the viewmodel performs validation.
- If validation fails, the viewmodel instantiates another viewmodel which stands for an error message, and the view chooses how to show it, e.g. with a modal dialog box or with a temporary “toast”.
- If validation succeeds, the viewmodel persists the entered information in the data store.
- If the user opts to cancel, then the viewmodel discards the edited information.
- There will be a viewmodel for this dialog box, which exposes:
- Viewmodels are so agnostic of presentation concerns that they can in fact be instantiated without any user interface at all. This allows us to test the entirety of the behavior of our application logic without a user interface and without having to examine any of its private implementation details such as its data store. Our application logic tests simply modify the public state of viewmodels and examine how the viewmodels further modify their public state in response.
- The presentation layer consists of views.
- In a desktop application, views are user-defined controls, panels, windows, dialogs, etc.
- In a web application, views would be HTML fragments.
- Each view type is specific to a particular viewmodel type, and contains bindings, which describe how each property of the viewmodel is bound to each property of a control within the view.
- So, a
CustomerFormview which is meant to display aCustomerviewmodel has a binding which specifies that theNameproperty of the customer should be bound to theTextproperty of a certainTextBoxcontrol within the view. - Note that these associations are purely declarative, and they reference nothing but statically available information, (data types and their members,) which means that they can be described using a markup language, i.e. without the need to write any application-specific code to build up the user interface.
- So, a
- A viewmodel may contain a property which is in turn another viewmodel. Let us call that a child viewmodel. In this case, the view can do one of two things:
- Specify a particular child view type to display that particular child viewmodel.
- Specify a mapping table which defines what type of child view to use for displaying any different possible type of child viewmodel.
- Views are resolved at runtime, based on the actual type of the child viewmodel, which can be more derived than the advertised type of the child viewmodel property; so, if a viewmodel exposes a
Customerchild viewmodel property which can be either of typeCustomeror of a more derived typeWholesaleCustomer, the mapping table can specify a different child view type for each of these child viewmodel types, and the right child view will be instantiated at runtime depending on the actual type of the child viewmodel. - Any child view can in turn contain its own mapping table which defines more associations, or redefines existing associations, so that for example:
- In the scope of an
AllCustomersview, theCustomerviewmodel can be associated with aCustomerRowview, so as to present the customer as a row in a tabular control. - In the scope of a
CustomerDetailsview, the sameCustomerviewmodel can be associated with aCustomerFormview, to present the customer using individual fields laid out on a surface.
- In the scope of an
- Note that again, this mapping table consists of nothing but statically available information, (view types and viewmodel types,) so everything is still achievable in markup.
- A child viewmodel property can be optional or nullable, thus allowing the application logic to control whether an entire section of the user interface is available or not at any given moment.
- A child viewmodel property can be a collection of viewmodels, allowing for a corresponding child view which is a list control or a tab control. Viewmodel type mapping still applies, so if the collection contains viewmodels of different types at runtime, the resulting list control will consist of different kinds of rows, or the resulting tab control will consist of different kinds of tabs.