MVVM, My Way
Killed the Post Before Publishing
After writing a good chunk of gibberish, I'd made some notes to myself about what I was going towards. One of the comments caused me to realize that what I was writing about wasn't really what I needed to be writing. Writing up a post on how my vision of MVVM is very drastically different than what MVVM actually is... I realized I was describing a different pattern.
The comment was:
Hmmm... I'm kinda thinking that my design is a View-Zipper-Mediator. The VZM. ... I'll stick with a strict MVVM for now; but... kinda liking VZM. Oh wait; Zipper has an 'er'... I'll have to find a better name for that layer. Ooo... Bridge. View-Bridge-Mediator; VBM.
Given that I feel there is enough distinction to be treated as a new pattern and not an interpretation of MVVM; I've ceased writing this post. I'll adapt some of it for future post(s); but this is to be considered abandoned. I really only had a few hundred words about testing left to go.
I'm working on a post about the VBM, it'll be put up soon. I'm posting this to provide additional insight into how I view using the VBM and a little of my thoughts that lead to the different pattern.
I don't recall seeing this particular presentation pattern structure before; I know other engineers who've used this structure in nameless form. It's not MVVM, and I don't want to be able to refer to it; hence VBM. If it exists elsewhere; I'd like to read up on it.
Below here is the original post which lead to the idea of the View-Bridge-Mediator
This is about how I apply the MVVM pattern. It deviates from how I've seen MVVM talked/written about and applied. I've found it fits the way I approach and work with populating and working with the UI.
I've read a lot and seen a few implementations; and none feel like solid, well defined implementations.
I see the other ways as lacking clarity on functionality goes where. The prevalent structure tends to be a pattern that lends itself to being abused and the code getting corrupted. Once this happens; there's no longer a pattern to stick to or follow.
My MVVM
MVVM is a way to structure User Interface implementation to avoid integrating business logic with UI Logic.
It's generally structured into 3 classes; which is how I've been using it. These should be thought of as Layers. If it makes logical sense to make multiple classes for each layer; by all means, do so.
In one of my current projects, the view has a control on it (well... duh...) and this control is it's own MVVM pattern. I'm not 100% sure I like making it a full MVVM; but I already see the value of it. I'll need to come up with some kinda "sub-MVVM" pattern for (at least) Android as the sub-view MVVM breaks a few of my MVVM layer separations.
There are going to be multiple, if not many, interpretations of what MVVM is; please search out those other versions. Read up on them, learn them, use them. Learn the strengths and weaknesses of them. Be more knowledgeable about what others have done.
This is my version.
For what I do; I find it to have the most strengths and mitigated the most weaknesses.
I make no claim to this being better... ehhh... How about no claim that it's perfect for all situations. :)
- Model
This contains the business logic and knows what data to get from the rest of the app. I consider this layer to be a Mediator. - View
This is the the UI layer. It only deals with providing the correct ui control to the ViewModel. - ViewModel
The ViewModel understands how the data from the Model maps to the ui components provided by the View.
The ViewModel understands what method on Model the UI actions map to.
IMO, it's very simple and straight forward. The ViewModel zips together the elements on each side so they don't have to know about each other. It should be stripped of as much logic as possible.
Databinder
I like the 3 layers of MVVM; I haven't found a use for the often mentioned 4th "DataBinder" layer. I can kinda see a place for it in the correct system, but I'd need some pretty hefty convincing. I have a few issues with applying the DataBinder technique with MVVM.
-
Logic in the View layer.
It becomes too easy to start including logic in the View layer. Not just the View's 'code-behind' layer, but into the view representation. In the case of Android; logic would be in the xml file. Having logic in the UI definition is about as close to the highest offense I'll find with code. I'll fight pretty hard to avoid putting logic into the UI. -
Removes Abstraction
It readily violates the hard boundaries of the MVVM. I grant that this is an issue because of how I view MVVM; but... Look at the post title; "My Way". Passing a data object into the highest level of the UI means that it is then tightly coupled to that representation of the data. The abstraction MVVM is intended to provide - *poof*. -
Encourages violating encapsulation
UI databinding normally doesn't allow a large feature set in what is written to bind data. And if it does; why the hell are you writing code in the UI?!?!?! ... ahem... Since there's not a lot of codability with the databinder, it encourages violating encapsulation. You'll need to use getters (I'm working on a post about object oriented programming encapsulation; where getters are bad) or just expose the naked values. It corrupts the data objects, or forces new data classes just for the MVVM. Writing data objects just for the UI to consume is going to turn the MVVM into a badly organized MVC.
Hardline
In the description of MVVM I briefly covered the responsibilities of each layer. Another hardline I take about the MVVM, which seems to run against most implementations, is re-usability. I look at MVVM as a logical collection of classes dedicated to showing a specific UI. This results in a few rules about implementing the MVVM.
-
No reusable code.
This is part of where my "Model" definition diverges from the rest of the descriptions I've seen. The fact I consider it to be a Mediator... also divergent.
The Model is not a data representation that's used elsewhere in the code. It's the knowledge of what data objects are required to populate the UI. What other MVVM's would put as the model; I have the model hold a reference to. I see no reason to create an entire DTO for each UI. Have the "Model/Mediator" get all the data the DTO would have been built out of, and provide the appropriate one to the ViewModel when required.
The code of an MVVM is specific to that View. It doesn't apply to other views. If there's some common functionality - Abstract it out into a base class. Pull it out into it's own class and use the Composite pattern.
There are going to be a lot of refactoring techniques that can be used to extract the common code into a reusable component; but then it no longer belongs as part of the MVVM. It's gets used by the MVVM. -
No logic in the View
This is closer to what the mainline MVVM pattern suggests; but I feel I go a step further with it. The View is responsible for knowing what control displays what data. Not how to display it; or how to change the control. Just which controls display what data. The view should be a collection of methods that return UiControls. These are used by the ViewModel to 'zip' (see, I love this analogy) together with the data from the Model. -
Only the Model talks outside the MVVM
Since most MVVM's have the Model as a DTO, the ViewModel; or heaven forbid, the View; has to handle the interactions with anything outside the MVVM layers. I set the Model as the only component of the MVVM that knows about anything outside the MVVM. This, of course, ignores the View taking user inputs. If there's data to be retrieved, the Model does it. If there's transitioning to a new screen to happen; Model does it. Showing a new view can be a little odd, as some platforms have requirements that make it simpler to do in some fashions. Android's preference for using an activity to start a new activity is one example. I'm fine with a View/Activity reference getting passed to the Model to perform an operation. Even getting passed outside the MVVM as a base class (Activity in Android) is acceptable. There should be no dependencies on any MVVM classes outside of the MVVM; this includes the Model. The model knows about the rest of the app; the rest of the app doesn't know about it.