MVVM is Exceptionally OK
ashfurrow.com
ashfurrow.com
I feel like MVVM is generally solving the wrong problem though. Ok, so MVC tends to get nasty because people shove all the logic into the controller, the model ends up being a crappy interface to the database, and views do too much. But maybe that just means that MVC is a bad fit for what you're doing. It doesn't mean "we" need the "next" MVC, it means you have to do the hard work of actually designing what your application does without training wheels.
Also helps in large organizations that develop lots of apps because it reduces the learning curve for new team members.
Besides, it's not like MVVM is a huge thing. It can be explained in a paragraph.
That literally applies to any program though. A compiler has to manage a lot of state too, but I've yet to hear compiler makers trying to figure out why they have so much logic in their controllers, and why their views are too big (because nobody would write an MVC compiler).
> Besides, it's not like MVVM is a huge thing. It can be explained in a paragraph
And yet there's like 500 blog posts about why you're doing it wrong, and how to do it better, etc. Of course, in truth it's not complicated, what makes it complicated is when it's applied in contexts that make little sense.
Model - Input -> output format transformation
View - input/output
Controller - Drives the model
And not much coupling between them except to pass data back and forth.
Sounds like a regular compiler to me.
Just looking at what you said though: why would you represent a transformation (something inherently a verb/action) as a model? "Model - Input -> output format transformation". It operates on models, it's not A model.
Also, claiming the view as input/output is pretty reductive. How do I represent a lexer as a view? More importantly, why would I want to?
Fun experiment, find the views and controllers in here: https://github.com/llvm-mirror/clang
shrug MVC / MVVM is that simple.
>why would you represent a transformation (something inherently a verb/action) as a model? "Model - Input -> output format transformation". It operates on models, it's not A model.
Models are not limited to being data stores. Models are responsible for doing work, so-called business logic. Controllers / ViewModels work with Views to take in user input and apply it to the models, then take the updated model and give it back to the user.
>How do I represent a lexer as a view?
The lexer is part of the model.
The view would be what the compiler uses for input/output: for a CLI compiler it would be the function that reads stdin or the filesystem + the function that writes to stdout and the filesystem, for a compiler hosted in an IDE or webpage would be the compiler library interface (ie: a programmatic way to give it files or strings and receive output + errors), and so on. Separate views that only know how to take in user input and display user output, interfacing with a common controller/viewmodel + model.
Edit: Minor clarifications.
An example of a MVC compiler would be something that abstracts the act of compiling bytes into bytes from how it obtains those bytes or puts them out.
I think gcc works like the former and clang like the latter? I'm not familiar with either.
An example I am familiar with is the TypeScript transpiler - a "Compiler" class with a pluggable "Host" interface. There is one implementation of that host for each environment it can run in (in-memory, node.js, cscript.exe) that knows how to read files and write output. The compiler class would call host.writeFile(filename, contents) and it's up to the host whether to write that to a file, or a textarea in a web page, or pipe the string to another transpiler, or whatever. It's up to the view to decide whether to colorize output or not, whether to show an underline that the user can click to jump to the error or not, etc.
Basically the view is independent of the model, and provides functionality local only to its interaction context that the model shouldn't need to care about. As a bonus it's pluggable and thus easy to write new views.
MVC is not simply "things should be abstracted from each other". That just, well, abstraction. (Or, better, indirected: http://zedshaw.com/archive/indirection-is-not-abstraction/ )
I've been anti-MVC (unless you are in the rare situations where it really works) for a while, and this is precisely why. It clouds people's thought. It convinces people they're doing "design" when they are often just complexifying things by rote application of badly misunderstood ideas.
Again, this is why I find the term loathsome as it is actually used. Not only do people not think about it, they, like, actively won't think about it. Anyone who actually tries to nail down "what exactly is MVC" will find themselves coming up empty. It is an undefined term, and should be discarded for pretty much everybody who isn't writing a CAD/CAM app, not used as some sort of vague template for "all good design".
You've progressively shrunk the goalposts in every post in this series until you can't help but hit MVC. That's not useful.
MVC is an example of separation of concerns, specifically applied to separating the computation of data from the input and output of that data. I don't see how this is such a difficult thing to understand.
(Edit: Heck, even Wikipedia calls it "a software architectural pattern mostly for implementing user interfaces (but not only for user interfaces). It divides a given software application into three interconnected parts, so as to separate internal representations of information from the ways that information is presented to or accepted from the user." Emphasis mine.)
>Again, this is why I find the term loathsome as it is actually used.
From this and sibling post I seem to detect some hostility against the term due to there being articles about "you're doing MVC wrong." You can find those a dime a dozen on any topic. Don't misdirect your anger.
>Anyone who actually tries to nail down "what exactly is MVC" will find themselves coming up empty.
See above, as well as my previous posts in this thread.
>You've progressively shrunk the goalposts in every post in this series until you can't help but hit MVC. That's not useful.
On the contrary, I've been consistent from the very first post what MVC is and how it applies to software. You're the one here insisting that it's something magic that needs to be different from what I've been saying it is.
Ontological modelling is the pure kind of abstraction—not the kind you reify by implementing it. There's no "MVC design pattern"; all systems can be broken down into MVC. You're never not implementing the "MVC design pattern."
The reason people get confused is that there are design patterns that people implement which make an explicit distinction that looks like MVC. This has nothing to do with MVC; you haven't "implemented MVC" by creating a system where all your model classes are Model classes, and all your views are Views. You've maybe made it easier to reason about your system in MVC terms, but that doesn't get you anything unless "reasoning about your system in MVC terms" is something you need to do a lot, because e.g. you and your coworkers split your responsibilities that way.
Now, what you may have done instead, when you think you've "implemented MVC", is implemented something like Hexagonal Architecture[1]: an actual design pattern—an idea that some-but-not-all codebases are an instance of—involving effectively inverting your "app" into an "engine" of business logic that relies on dependency-injection of engine-specified "pluggable" interfaces for its IO (both the out-to-the-world kind, and the in-to-its-databases and third-party-services kind.)
If your MVC app's "core" business-logic part is a library with direct unit tests on it and so forth, and this part is useless on its own; and if you've got another part that treats the core as a dependency, and turns it into a web service or whatever else—then you've got Hexagonal Architecture. You've also, coincidentally, got some quite-elegantly-delineated MVC boundaries: your model and your view can't share logic, since there's a component boundary between them and neither one can assume anything about the other. But that's just an analytic property of the code; you didn't "do MVC" or even "do MVC right." You just did Hexagonal Architecture, which happens to make it easy to reason about your codebase in MVC terms.
No, but they do write entire books about how their parser uses recursive descent or LL(1) or about how they do code generation.
In MVVM you have the Model and View layers, which I get, and also the View Model layer, which I don't. The View Model is supposed to be UI independent, and that I don't get. The Model layer is already UI independent. There is no need for a second UI independent layer. In practice the View Model layer is written specifically to support the UI, so there is no point in making it UI independent. Some argue this is for testability. But you can already test the Model layer without the UI. Additional testing should include the UI, and should be through the UI.
The motivation behind MVVM appears to be to update the UI automatically when data changes. Modern frameworks are moving away from two-way data binding (React, Angular 2.0, Ember, etc.)
Also, data binding is best done in code (the jQuery way) as opposed to declaratively (the WPF way).
The ViewModel just adapts various Models/Services for a particular View. It converts types provided by the Model as needed, formats strings, computes progress, etc.
"UI independent" just means that it should be a logical model of the View, rather than a visual model (e.g., don't expose Color or Brush properties, instead expose properties like IsSelected). So think of the View model like logical code-behind.
> Modern frameworks are moving away from two-way data binding (React, Angular 2.0, Ember, etc.)
On the web, maybe. But Windows apps (UWP) are still based on data binding.
Other than XAML being somewhat confusing and having relatively poor IDE support, I've found the WPF MVVM model to be the most intuitive and nice UI design pattern that I've used.
Why not? The ViewModel is written specifically for the View, and it does not serve any purpose to make it UI independent. By avoiding Color etc. in this layer you end with two UI independent layers (Model and ViewModel) and that makes no sense. One UI-independent layer is enough.
In MVVM the View knows about the ViewModel but supposedly the ViewModel does not know about the View. But in reality the ViewModel is written specifically for the View, so it might as well know the View.
Yes, I understand that Windows APIs still use two-way data binding. But by and large the developer community is realizing that two-way data binding is gimmicky and that in large applications two-way data binding is a liability. On the Web most JavaScript frameworks are moving away from two-way data binding (React, Angular 2, Ember, etc.) The reason for that has nothing to do with the Web or with JavaScript. The underlying reason is applicable to application development in general, regardless of platform.
Here's Tom Dale and Yehuda Katz, developers of Ember, explaining their decision to move away from two-way data binding: https://changelog.com/131/ (skip to 0:42)
Excerpts:--------------------
"Why do people still prefer to write server-rendered apps? The programming model is just so easy. If you think about how people build sever-rendered apps.. the request comes in, you get your model data out of the database, you hand it to your view layer to render, you return that output and that's it. Every time you handle a new request, because HTTP is stateless, you start from scratch.
"Conversely, things like Angular and Ember have two-way data bindings, right? And it really easy to end up in -- especially in a large or sophisticated application which is stateful. As the user is looking at it, it's not like the state is getting reset, you are constantly having to keep everything in sync yourself. It is easy to end up in a state where you can't yourself explain how data flows through it.
"The brilliance in react is bringing back a programming model that is as simple as server-rendered apps.
"Even though both Angular and Ember have the notion of events and data bindings, data bindings feels so cool that people started tunneling events through data bindings.
"Honestly when I look at the critiques React people have about Ember and trying to understand, OK, you were an ember developer, you were reasonably productive, but you find yourself way more productive in React. What is happening? What we are finding -- and this played into the Ember 2.0 plan -- is that people are abusing two way data bindings to express something that is fundamentally an event. A big focus of Ember 2.0 is moving away from two way data bindings as the primary method of communication to events. We added too much sugar around two-way data bindings and that led people to use two-way data bindings as an event bus.
In Knockout, you have HTML templates, which are the view, but the distinction between the Models, the View Models, and every other bit of code in your application is pretty much lost. Not sure if it was my lack of knowledge of MVVM best practices (likely), Knockout (possible), or something inherent to MVVM, but if ever there was an architecture that lent itself to turning into Big Balls Of Mud...
Edit: Digging into the links, it seems the article is talking specifically about native mobile applications, so maybe there's context I'm missing.
Do you have any reference about this? I thought the opposite was true. My main interest being Ember, I'd like to know more about what you said.
Angular still has ngModel according to its docs. Whether you have two-way binding or not, View Models are still useful. Cycle.js doesn't have two-way binding but uses Event Streams and has a pattern that is similar to MVVM (they call it Model-View-Intent but the Model is a View Model by normal definition).
React not having something like MVVM makes it difficult to create reusable components. Most React apps are divided into the view layer (which are "components" but really just dumb views) and application logic which lives in the flux layer. This division means you can't / shouldn't write components that fetch their own data.
For example under iOS this probably means going all-in with ReactiveCocoa. But I'm not convinced that completely switching paradigms like that is the right way to go.
Not even close. The model does everything in MVC apps. Views display the model and react to model changes. Controllers pick up the slack, which shouldn't be very much.
Now in Model Widget Controller[1] the "view controllers" (something that doesn't even exist in actual MVC) have to do more work because the widgets are generic. Still, if you find your "view controller" doing "everything", you've pretty much missed the point of MVC. Completely.
[1] http://blog.metaobject.com/2015/04/model-widget-controller-m...
Other classes, like NSLayoutManager, look a lot like most of a view controller. But they're not officially a subclass of UIViewController, because they don't link directly to a view - even though they could have been designed to.
Apple makes a big deal of MVC, but I sometimes wonder if anyone knows what MVC really is - except possibly a design pattern for generating disagreements about MVC.
Maybe there is something fundamentally different about web development and/or JavaScript that causes it to not work well?
For example, lets say you have a User model object. You want to display information about the user in three different places in the user interface. With MVVM this is simple. You have a single User model and three different VM instances that each point at the source model. Each VM has a matching View. Any change in the User model propagates down to the VM and then the View. If you edit the users name in the View it updates the VM and then the User model. Changes then cascade back down so the other two views update if needed. Very simple.
But how is this done in Flux/Redux? Does the store contain the user information just once? But then what about the intermediate information that is the equivalent of the three views? I presume you need to store this state information in some store as well. But then how do you handle updating the user and having the three other stores updated at the same time as needed? I am not a web developer so this might be trivially simple to those that know how it fits together.
In a large application using two-way binding, the application state is implicit in its execution. There's thousands of places that mutate the state, so many that if you tried to make a graph of the dataflows, you'd get spaghetti. Sure, the code is neat and simple, but you know you've got a problem when the dependency graph is so complex that you can write a cyclic binding and not even realise it.
Redux borrows a lot from functional techniques to tackle these kinds of problems. The big one is that you can't mutate the state, because it's immutable. Instead, you write reducing functions that take an application state and an action, and return a completely new application state based on that action (which might sound wasteful until you learn about functional data structures).
This buys you a lot of things. You can hang on to your old application states instead of throwing them away, and then, very easily, set up a slider that lets you "time travel" in your application. You can grab a list of actions, and feed them to a hundred instances of your application, all with different initial states, and inspect how the state changed at each action- really useful for testing or debugging. You can completely change your code, and have the application update in real time with all the state intact, but now running your new code- this really speeds up development. You can catch errors, and then automatically send a complete snapshot of the application state (complete with where the focus was, the text the user was typing, the clicks they used to get to that particular screen, etc.) and then play it back, allowing you to perfectly reproduce the error.
These things are all extremely difficult when the state of your application (and how it changes over time) is implicit and scattered over hundreds of views. It's trivial when the state of your application is completely described by an initial state, a series of actions, and a single reducing function.
Regarding the "intermediate information" you alluded to: this is generally solved by using something like reselect, which allows you to define transformations on the state that can be fed to views. For example, you could write a function that returns a list of items in the state that match a search query in the state, but not have the resulting list itself live in the state. This means you still have one source of truth, and the immutability of the state ensures you don't lose any of the benefits I listed above.
"Lets say you have a User. You want to display information about the user in three different places in the user interface. With Redux this is simple. You have a single User store and three different containers that each receive the source model as inputs, transform it using connect/reselect, and then passes it on, along with action creators to change the data, to the underlying components. Any change in the User model propagates down to through the containers to the components. If you edit the user from one component it updates the store and then the components. Changes then cascade back down so that all three containers update if needed. Very simple."
Sounds familiar, no? The big difference is that instead of editing the data you're passed directly, and having it magically update everywhere, you're passed an immutable User object and an updateUser() function, which you call to edit the object.
But the same distinction between your data, the view, and the intermediate layer that transforms the data for the view is very much present.
MMVM sounds like a mutable inheritance based architecture.
Would love to dig deeper, but here is "how I feel"
https://evolvingtrends.wordpress.com/2015/12/03/why-react-na...
There's nothing about MVVM that says you have to copy Model state in your ViewModel either.
View -> ViewModel -> Service
and the data goes
Service -> ViewModel -> View
However, the same app state may be presented in different ways in different parts of the UI. Those different ways could each have their own ViewModel for transforming that state as needed for its presentation.
Poorly architected MVVM apps have lots of inter-ViewModel dependencies... that's where you wind up with state spaghetti.
I really love value semantics with structs in swift, it's almost always the right thing, and generally very clear when it isn't.
If anyone ever programmed so that
int a = 15
int b = a
b = 17
assert(a == 17)
meant that a was now 17 they'd call it insane, I have no idea why anyone would want that as the default behavior.Since it's UI code that usually breaks when exposed to weird patterns just write something that sends random touches to the screen in a rapid manner, it surfaces the bugs really quickly and generally disallows bandaid patches.
I find regular UI unit tests on the verge of useless, it's generally just coding the pattern the developer moves through the UI anyway, eg. fill first form, second, press submit, not random stuff in 3rd form, press submit while simultaneously pressing the back button, then move forward back into the same controller.
eg. If you press a text field and the back button at the same time does it leave the keyboard up when navigating back? That's where the real bugs are.
The solution is extensions and inheritance that cut down on boiler plate in the VC, as well as child view controllers.
Chuck your models into your views and call it a day. For example take your ViewCell, add a method called setData: / setModel: / configureCell: whatever you want to call it, and put your UI manipulation code in there. The puritans will tell you you're doing it wrong but it declutters the code into a bunch of 25-100 line views.
If you really need a lot of logic in there make a ViewController for that view and add it as a childViewController.
MVVM and MVC work well if you're doing it right, which most people aren't. We've all seen the code where they started with MVC and made a bigger mess moving to MVVM because they didn't do MVC right in the first place.
My rule of thumb is to grep for .tag, if you see more than one or two references to view.tag the code is totally fucked. If you see switch(view.tag) just run.