Upcoming and OnDemand Webinars View full list

Shades of MVVM

Bill Phillips

This September I went to Droidcon NYC in Manhattan, down in the Financial District.
One talk that got my mind going was given by Florina Muntenescu.

It was called A Journey Through MV Wonderland.
The talk is a lucid explanation of the relationships between MVC, MVP and MVVM. Check it out:

A quick aside before we get started:

What set my particular brain off, though, was the discussion of MVVM.
The whole talk is enlightening, but I want to call particular attention to this diagram:

MVVM as described in A Journey Through MV Wonderland

(Original slide here)

I’ve been interested in what people are actually doing when they’re writing MVVM-architected apps.
So I found it nice to see one clear interpretation of MVC, MVP and MVVM in this talk.

That’s not always the case, though.
Depending on who you’re talking to, MVVM can mean different things.
In this post, I’m going to present a few approaches to MVVM used by people I’ve personally talked to—folks in the data binding community, in our team here at Big Nerd Ranch, and from Florina’s talk.

My point won’t be to figure out which of these is correct or right, but just to show what I’m familiar with. Correctness is more of an admirable goal in these arch discussions than a useful yardstick. So if you read one of these diagrams and think, “Oh no, I’m/we’re doing it wrong”— chillax. If your code runs, the people using your app are happy, turnover is low and nobody’s lying to themselves about anything important, then you’re doing it right. Those are the only yardsticks that truly matter.

Classic Binding ViewModel

Binding MVVM

When we first started implementing MVVM in our projects, we implemented something like the above, which I’m calling “classic binding MVVM.” (I’ll explain why in a moment).

In this model, the ViewModel exposes methods that do one of two jobs:

  1. Reveal display data, or
  2. Invoke actions from the user interface.

We first tried this out in an experimental Kotlin project called KöffeeKüp, an app that helped us keep track of how frequently we were brewing coffee.
One ViewModel was called CarafePlacardViewModel, and had methods that looked like this:

    @Bindable
    fun getCarafeDrawable(): Drawable {
        var timeSinceLastWashMs = clock.currentTimeMillis() - lastWashTimeMs

        var resId = CarafeMentalHealth.getDrawableResId(timeSinceLastWashMs)
        return context.resources.getDrawable(resId, context.theme)
    }

    fun resetTimer() {
        lastWashTimeMs = clock.currentTimeMillis()
        timeStore.setLastWashTimeMs(carafe, lastWashTimeMs)
        notifyPropertyChanged(BR.carafeDrawableResId)
    }

The first method changes what image is displayed depending on how old the coffee is.
(The older the coffee is, the grodier the carafe image gets.)
And the second method is invoked when the button for the timer is invoked.

In this way of doing things, everything can be wired up from the layout file using the Data Binding framework:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="viewModel"
            type="com.bignerdranch.android.koffeekup.viewmodel.CarafePlacardViewModel"/>
    </data>

    ...

                <ImageView
                    ...
                    android:src="@{viewModel.carafeDrawable}"/>

    ...

            <Button
                ...
                android:onClick="@{() -> viewModel.resetTimer}"/>

    ...
</layout>

The reason I called this “classic binding ViewModel” is that this is roughly the way the architecture was originally pitched when it was originated in Microsoft’s tool stack.
John Gossman pitched it all the way back in 2005, and it has been recommended for apps that use data binding ever since.

Rx MVVM

A closely related version of the same idea is to use the same MVVM architecture, but instead of using Data Binding to glue your ViewModel to your View, you use RxJava Observables.

RxMVVM

The difference between this and the classic binding VM approach is all in the implementation. The Binding VM approach relies on Data Binding in your layout file; here, you write code in a View class, and rely on RxJava to communicate with your ViewModel.

So CarafePlacardViewModel would instead look like this:

    fun getCarafeDrawable(): Observable<Drawable> {
        return Observable.interval(100, TimeUnit.MILLISECONDS)
            .map { time -> clock.currentTimeMillis() - lastWashTimeMs }
            .map { timeSinceLastWashMs ->
                val resId = CarafeMentalHealth.getDrawableResId(timeSinceLastWashMs)
                context.resources.getDrawable(resId, context.theme)
            }
    }

    fun resetTimer() {
        lastWashTimeMs = clock.currentTimeMillis()
        timeStore.setLastWashTimeMs(carafe, lastWashTimeMs)
        notifyPropertyChanged(BR.carafeDrawableResId)
    }

CarafePlacardView would be a proper Kotlin (or Java) class that would wire up to the appropriate Observables:

class CarafePlacardView(..., private val viewModel: CarafePlacardViewModel) {
    ...

    fun setup() {
        viewModel.carafeDrawable.subscribe { drawable ->
            placardImage.imageDrawable = drawable
        }
        resetTimerButton.onClickListener = { view ->
            viewModel.resetTimer()
        }
    }
}

The responsibilities of the two classes are exactly the same.
The only difference is how they’re implemented:

  • Instead of using Data Binding’s Observable interface with @Bindable getters, RxJava Observables are used.
  • Instead of using a layout file with Data Binding wiring within it, a View classes is implemented in code.

I can’t speak to the advantages of this pattern over the Data Binding version, but it appears that it’s a nice option if you don’t want to dive in with Data Binding.

MVVM…C?

In my discussions with other developers, I’ve found that a few folks have independently started using a variation on the classic binding VM pattern.
I have not found any blog posts documenting it, so I’ll talk about it here.

At Big Nerd Ranch, we felt like our ViewModels were starting to get a little too complicated.
So we decided to pull out the user actions into our fragments:

RxMMVVMCVVM

While we have used this approach, I don’t know how much I can recommend it.
This appears to have pushed a lot of code into our fragments, which isn’t under test. I don’t know if that’s a consequence of this pattern or if it’s unrelated, but it does look a little grody in the diagram—the fragment is taking on a lot of jobs here.

Outside of Big Nerd Ranch, people have been taking this one step further by defining an interface for the user actions in their layout file:

MVVMCallback

This is the approach taken by the majority of people I know who are on the leading edge of working with data binding.
In this way of doing things, more care is taken to separate out the callback and its implementation.
It’s isolated completely from the ViewModel, and the layout file is abstracted from the implementation by an interface.
I haven’t tried this myself, but it looks like a solid approach.

So What Do I Do?

Well, if you asked me to choose an architecture pattern up front today to guide my work on a new project, I’d go with the classic binding ViewModel approach.
I’ve worked with it a little bit already, and I found it to be a good friend in improving my app’s testability. I’m really interested in integrations with RxJava, though—I’d love to be able to integrate that into my use of data binding somehow.

Even though that’s what I like, though, I do not recommend rearchitecting your existing app based on my recommendation, just because I said it is the “best”.
Evaluating architectures in that way is misleading and and often frustrating.
You can find opinions like mine easily, but head-to-head comparisons between different architecture patterns with working code are much harder to find, much less compare. The goodness or badness of architecture has everything to do with how your code grows and changes within that architecture, so these snapshot comparisons are almost useless as individual datapoints. That often makes discussions of architecture are extremely annoying.

And yet: I love hearing about application architectures. I see at least two or three arch oriented talks at every conference I attend. What makes them great is when they tell a story about how a group of engineers encountered a series of challenges, and how they adapted their application to meet those changes.

There is no spreadsheet to compare these stories in, or metric to ascertain which is “best”.
Those stories may shed light on your situation, or they may not.
They’ll never be identical to yours, though, because architecture is as specific as your team itself is.

So that’s what I really recommend: don’t take any of this gospel. Ask, “What problems do I actually have with keeping this codebase clean? Can one of these approaches solve it?” And until then, just keep reading.

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project