czwartek, 17 grudnia 2015

"MVVM is not very good" - How do you make it better ?

Soroush has created a blog post that describes my funny feelings when using MVVM.

MVVM is an evolution of MVC. It's "better" than MVC, because it reorganizes the way you layout your logic between the modules. View and Model are very clear in their purpose, so where did all the rest of the logic go ?

The ViewModel naturally. As Soroush states, you just push all of your code from the ViewController to the ViewModel and how does that solve anything really ?

What if I told you...

What if I told you that design patterns are just ideas, and you should never follow the blindly ? The problem with design patterns, tutorials and examples that describe them are written in the void. They usually contain a couple of lines of code that don't exist in context of a big system. That's why MVVM looks great in case of a simple tutorial timer interface.

The problem appears, when you start using it in an actual app that does a lot of things. You end up with a ViewModel that contains business logic, sorting, validating, calls to database model, calls to the networking model. You start to realize that you have an class that's called ViewModel, you can't really say what it does. That spits at Single Responsibility Principle.

Does that mean that MVVM is bad and you should not use it ? No. you're going to run into the same problem using VIPER. You'll have tons of classes named Presenters and they'll all do a lot of different things. Sure, they'll follow SRP a little better, but they still won't be perfect.

MVVM, MVC, VIPER are just paradigms and you as the architect of your system have to choose explicit design that's best for your context. 

It's hard, but it's beautiful - it means that you have to be creative, you still have a lot of work to do. Just because someone invented MVVM it doesn't mean that you're free of thinking about architecture.

So how do you do it right, while following MVVM ?

Anything can be your ViewModel - ViewModel is just an idea. It's just something that tells you: "Hey, stop putting logic into your view". ViewModel is just a class with a couple of properties and commands and that's it - it doesn't say anywhere, that you have to put everything in one class.

You can have a lot of ViewModels for one view - That's the main sin of ViewModel tutorials. They create one big ViewModel for each View and that's it. It works for simple tutorials, but doesn't for big applications.

Follow the Single Responsibility Principle - You should be able to describe what your ViewModel does in a very short sentence. It'll help you decide how many ViewModels you need for one view.

Name your ViewModels accordingly - Naming things in programming is hard, I know, but anything other than XViewModel is better already.

Think of your ViewModel as a Mediator (design pattern) - If you need to. It mediates between all sorts of different modules and presents the data gathered. It's very useful for collections for example. It's hard to divide logic of a collection, between different ViewModels, because it has to be presented in one particular place. In short, you can't have 3 ViewModels for sorting the collection, fetching the collection locally and fetching the collection remotely, because the View is bound to only one ViewModel.

MVVM Card Game

Very simple and quick example.

Requirements:

• You have a view with a table of cards
• User can put cards from a deck on the table
• The table's function is to sort the cards
• The table accepts carts that values sum to 100
• The sort result is saved to the server after sorting

Blindly following MVVM you'd put that all in CardTableViewModel, but that's bad. Here's all modules you define instead:

• UsersHand (ViewModel) - Contains logic for user's cards in the deck. Used to draw and put cards back.
•• UsersHandLocalStorage - Used by UsersHand to store the user's hand in the database.

CardsTableMediator(ViewModel) - Mediator for sorting, validating, storing the table locally and sending it to the server. It presents the table to the view, after mediating the data between all the modules.
•• CardsTableCollection, CardsTableLocalStorage, CardsTableRemoteStorage, CardsTableValidator, CardsTableSorter - Modules used by the mediator to present the table.

So, we don't have a huge ViewModel, and it's still MVVM, isn't it ?

Stop following patterns blindly and be the architect of your app.