DM-VM-CM-VC-V*:
OSX complex application challenge
renamed into MVVM-C
(MVVM + Context Mgr)
Xavier LASNE
XL Software Solutions
(*): Data Model - View Model - Context Manager -
View Controller - View
14 Avril 2016
Complex Application Challenge
When you have:
A large number of View Controllers
which are depending in multiple ways from a complex data model
and this data model has a complex hierarchy where a single update trigger a
flow of dependent updates
including background activities triggering additional refresh
and you want to solve all this without additional frameworks which will
bring their additional complexity
then you need a strong architecture which will keep
your software simple and easy to maintain and debug.
- Une application commence de façon simple, mais se complexifie au fur et à mesure de ses évolutions.

- Utilité d’avoir une feuille de route expliquant comment métamorphoser progressivement son application au fur et à mesure par refactorisations successives.

- Le but est d’avoir une architecture simple pour le plus complexe des cas.
Architecture Goals
Minimise number of lines: Nice to have.
Clear definition of the role and responsibility of each component: Mandatory
Clear definition of the possible interactions between components, and of the
context in which they occurs: Mandatory
Clear definition of Data ownership and life-cycle ( Storage / Read / Write /
Processing / Display): Mandatory
Minimise dependency between components, threading issues, synchronisation
issue, race conditions, “not yet instantiated view controller” issues: Mandatory
Avoid exponential number of unnecessary refresh, loss of focus, refresh/
update/refresh infinite loop
Use simple and basic pattern to minimise learning curve and reduce side effect
issues.
Solve complex problem with simple solutions
Keep it simple
Revenge Development is
never a straight line. It’s a
forest. And like a forest it’s
easy to lose your way, to get
lost, to forget where you
came in.
Hattori Hanzō
Sequence Life Cycle
Main thread
Input Action
Update
Data Model
Update
Context Data
Display
Request
Data
Model
User action
or
background
completion
Write
Now, let’s start display!
data model updates
are forbidden
Read
Refresh UI
Contextual data define
what shall be displayed
One Unique Refresh per VC
1: VM
background
2: DM
3: Context Mgr
4: VM
5: VC
- Event driven model

- Eviter les cycles

- Eviter les rafraîchissements inutiles

- L’update du data model ne déclenche pas le rafraîchissement de l’interface, car des updates multiples entraîneraient des rafraîchissements multiples.

- Séparation entre Model et Contexte

- Comparaison avec un coeur: systole = écriture, diastole = lecture. Cycle poumon = update du data model vers un état consistent, Cycle organes = propagation des
données consistantes vers le reste du programme.

- Toute cette chaîne doit s’executer séquentiellement dans le main thread, afin que les écritures soient terminées et que le data model soit cohérent lorsque la phase de
lecture commence.
Method Naming Convention
Input Action
Process Data Model Update
Refresh Context
Notify UI Refresh
1
2
3
4
update
change
set
perform
trigger
refresh
notify
display
No Mix Boundary
- Rule 2: No “modifier” during refresh phase
- Rule 1: No “refresh” during update phase
UI Display5
Component Ownership & Interface
Application
Window C.
View Controller
Hierarchy
View
Hierarchy
Data ModelContext Mgr
View
Models
Data
Hierarchy
(0-1) - 1N - 1
View Models are owned by Data Model, not View Controllers
View Models are always present -> Solve synchronisation issues
There is a 1 to 1 weak relationship between View Controller and View Model,
or a 0 to 1 when View Controller does not exist.
1
N
Window
- Résout le problème de la gestion mémoire et des dépendances

- Pose le problème de l’association VC - VM -> Le VC s’enregistre auprès de son VM

- Impose un chemin pour les messages: VC - VM - DM/CM
Data Ownership
VC
VM
Shared
VM
Data
Model
Owns views and non-shared dynamic data
Dynamic storage of processed data =>
F(model data, context data)
Non shared, non stored, contextual data
Shared dynamic storage of processed data
Repository of model data,
archived in NSDocument
Context
Mgr
Repository of shared contextual data,
archived in State Restoration
Input - local data
Input
processing
Input
View Controller View Model
Data Model
Context Mgr
User Input
No Model or Context data update
UI
Display
Display UI
Processed
Data
Get
Processed
Data
Get
Data
Input - Model Data Update
Input
processing
Input
Source View Controller
Input
processing
Source View Model
Data Model
Context Mgr
1: Update Model
Data
User Input
If no model data update, go to Refresh Once step
or External Input
Data Model Update
Input
processing
Input
Source View Controller
Input
processing
Target View Models
Data Model
Context Mgr
Update Model
Data
Process Data
Process / Store / Background + Network Activity
Processed
Data
Source View Model Push
Data?
- Le problème du target VM est: Est-ce que mes processed data sont valides ou non ? L’information nécessaire est une information de validité. 

- Il manque les données contextuelles associées, et les données complémentaires du data model. Le view model ne va rien faire des données poussées.

- Mon approche préfère le “data-polling” au “data-pushing”. Only valid for simple one data = one refresh scenario.

- “Premature optimisation is the root of all evil”. Donald Knuth
Data Pushing
If 1 data = 1 atomic processing = 1 refresh
VM VCDM
KVOKVO
View
Bindingupdate
But if architecture evolves and introduce dependency between
several data, synchronisation and multiple refresh issues impose
refactor toward data pulling (or usage of more complex frameworks)
One notification should not mean “this data has
changed” but be a CONTRACT from Data Model
allowing to read a defined set of consistent variables
(= a state) during the current event cycle
Mixed Data Push - Pulling
VM observe either update value or set processing
dirty, then perform processing on read.
DM VM
Context Mgr
VM
2: refresh
1:
update
3: notify
KVO
VC
4: read
5: Process on read
- Définir des ensembles de données dépendantes

- Après qu’une ou plusieurs de ces données ai été mises à jour, déclencher le rafraîchissement de l’ensemble.

- Le refactoring de KVO vers “block update” n’est pas naturel, car il passe d’un mécanisme de “push-data” à un mécanisme “pull-data”.

- On considère par la suite les données “complexes”
Data Pulling
VM read Data Model on VC request
DM
VM
Processing
Context Mgr
VM
2: refresh
1:
update
3: notify
5: read & process
VC
4: read
Chosen option: simple calls without
complexity or performance penalty
Context Data + Refresh Step
Input
processing
Input
Source View Controller
Input
processing
Source View Model
Data Model
Context Mgr
1: Set Model
Data
Refresh Once
2: Set Context
Data / Notify
Trigger only 1 notification per UI Refresh !
Progress
Notify
UI
Display
Target View Controller Target View Model
Data Model
Context Mgr
Refresh Notification
Refresh
UI
Notify
Hierarchical, ordered refresh of the targets paired VM/VC,
depending on Context Data and Model Data dependencies.
Processed
Data
2:
Refresh UI
?
Context Mgr
Updated Data VC1 VC2 VC3
Data 1 x x
Data 2 x x
Data 3 x
Data 4 x
func onDataSetXUpdate() {
viewModel1.refreshVC1()
viewModel2.refreshVC2()
}
Dependent VC upon data set update
Data Set X
- Un Data Set est un ensemble de données du data model + context mgr lus par une fonction de refresh.

- Le but du context manager est une sorte de KVO étendu, qui appelle la fonction de refresh lorsque le data set a été modifié.

- Mon implémentation est statique, hard codée, mais Nicolas Bouilleaud a suggéré de la rendre dynamique, avec un VM qui s’abonne à un data set.
and Display UI
UI
Display
Target View Controller
Refresh Notification
Get UI
Processed
Data
Target View Model
Data Model
Context MgrProcessed Data
2: Refresh
UI
Get
Context
Data
Display UI
If View Controller exists in the VC hierarchy
then refresh its UI
Get Model
Data
Full Picture
Input
processing
Input
UI
Display
Target VC
Input
processing
Refresh
Notification
Get UI
Processed
Data
Target VM
Data Model
Context Mgr
Processed Data
Refresh UI
Get
Data
It seems complex (many calls), but it is simple
because it is the same pattern
for ALL the VC/VM/DM/CM interactions.
1: Set Model
Data
2: Set Context
Data / Notify Progress
Source VC Source VM
- Chaque composant a un rôle défini et limité. Les functions sont simples et courtes.

- Il dispose des interfaces lui permettant de l’accomplir

- On peut gérer / partager le développement à plusieurs.

- Le context manager semble être central, mais il a un rôle très macroscopique. Il ne sait pas si les VC sont présents ou non, il ne fait que coordonner les dépendances
et le rafraîchissement.
View Model central role
Input Management: Convert and
Forward input request to Data Model,
then Context Manager.
Refresh: Forward refresh requests from
Context Manager to View Controller, if
present.
Processing: At View Controller request,
process data from data model and
context manager into “ready to display”
processed data. Clear this data on VC
dismiss.
Input
processing
Refresh
Notification
Processed Data
VC
View Model
VC
VC
CM
DM
CM
DM
CM
Only use simple calls. No framework dependency
- Possibilité de partager les fonctions Input/Processing dans des shared view model, composants intermédiaires entre Data Model et View Model.
Conclusion
Let’s have a demo:
The demo is available on github: MVVM-C
https://github.com/xlasne/MVVM-C
Why not storing the context data into the
view controller hierarchy ?
Because each View Controller would then depend strongly on its
ancestor chain, making hierarchy change more difficult.
Because if the data is not in its direct responding chain, it may be in a
VC which is not instantiated. A contextual value shared between to
child is thus located in a common ancestor, which has no direct
interest in this value, or requires additional VM/VM interactions
Because it increases the number of connected/dependent components,
hence the number of sequences / tests / bugs.
Because it increase the VC size, and VC already have a lot to manage.
And for these gains, I am ready to pay the price of replicating the
view hierarchy into the dependency management of the Context Mgr.
Isn’t VC-VM one to one relationship prone to code
repetition, unnecessary proxy to data model ?
Yes it is. But it forces you to clearly decouple the application
logic from the view controller logic. Data write and read logic
is more clearly exposed than hidden inside a view controller.
And this extra step is what you would need to do if the data
become shared: doing it clean first is not a big work, and it is
“final”.
And if you want to refactor this logic into a shared view
model, the refactoring is easy.
And it is like a drug … once you have tasted it, you can not
resist to its appealing simplicity.

OSX Complex Application Challenge Architecture

  • 1.
    DM-VM-CM-VC-V*: OSX complex applicationchallenge renamed into MVVM-C (MVVM + Context Mgr) Xavier LASNE XL Software Solutions (*): Data Model - View Model - Context Manager - View Controller - View 14 Avril 2016
  • 2.
    Complex Application Challenge Whenyou have: A large number of View Controllers which are depending in multiple ways from a complex data model and this data model has a complex hierarchy where a single update trigger a flow of dependent updates including background activities triggering additional refresh and you want to solve all this without additional frameworks which will bring their additional complexity then you need a strong architecture which will keep your software simple and easy to maintain and debug. - Une application commence de façon simple, mais se complexifie au fur et à mesure de ses évolutions. - Utilité d’avoir une feuille de route expliquant comment métamorphoser progressivement son application au fur et à mesure par refactorisations successives. - Le but est d’avoir une architecture simple pour le plus complexe des cas.
  • 3.
    Architecture Goals Minimise numberof lines: Nice to have. Clear definition of the role and responsibility of each component: Mandatory Clear definition of the possible interactions between components, and of the context in which they occurs: Mandatory Clear definition of Data ownership and life-cycle ( Storage / Read / Write / Processing / Display): Mandatory Minimise dependency between components, threading issues, synchronisation issue, race conditions, “not yet instantiated view controller” issues: Mandatory Avoid exponential number of unnecessary refresh, loss of focus, refresh/ update/refresh infinite loop Use simple and basic pattern to minimise learning curve and reduce side effect issues. Solve complex problem with simple solutions
  • 4.
    Keep it simple RevengeDevelopment is never a straight line. It’s a forest. And like a forest it’s easy to lose your way, to get lost, to forget where you came in. Hattori Hanzō
  • 5.
    Sequence Life Cycle Mainthread Input Action Update Data Model Update Context Data Display Request Data Model User action or background completion Write Now, let’s start display! data model updates are forbidden Read Refresh UI Contextual data define what shall be displayed One Unique Refresh per VC 1: VM background 2: DM 3: Context Mgr 4: VM 5: VC - Event driven model - Eviter les cycles - Eviter les rafraîchissements inutiles - L’update du data model ne déclenche pas le rafraîchissement de l’interface, car des updates multiples entraîneraient des rafraîchissements multiples. - Séparation entre Model et Contexte - Comparaison avec un coeur: systole = écriture, diastole = lecture. Cycle poumon = update du data model vers un état consistent, Cycle organes = propagation des données consistantes vers le reste du programme. - Toute cette chaîne doit s’executer séquentiellement dans le main thread, afin que les écritures soient terminées et que le data model soit cohérent lorsque la phase de lecture commence.
  • 6.
    Method Naming Convention InputAction Process Data Model Update Refresh Context Notify UI Refresh 1 2 3 4 update change set perform trigger refresh notify display No Mix Boundary - Rule 2: No “modifier” during refresh phase - Rule 1: No “refresh” during update phase UI Display5
  • 7.
    Component Ownership &Interface Application Window C. View Controller Hierarchy View Hierarchy Data ModelContext Mgr View Models Data Hierarchy (0-1) - 1N - 1 View Models are owned by Data Model, not View Controllers View Models are always present -> Solve synchronisation issues There is a 1 to 1 weak relationship between View Controller and View Model, or a 0 to 1 when View Controller does not exist. 1 N Window - Résout le problème de la gestion mémoire et des dépendances - Pose le problème de l’association VC - VM -> Le VC s’enregistre auprès de son VM - Impose un chemin pour les messages: VC - VM - DM/CM
  • 8.
    Data Ownership VC VM Shared VM Data Model Owns viewsand non-shared dynamic data Dynamic storage of processed data => F(model data, context data) Non shared, non stored, contextual data Shared dynamic storage of processed data Repository of model data, archived in NSDocument Context Mgr Repository of shared contextual data, archived in State Restoration
  • 9.
    Input - localdata Input processing Input View Controller View Model Data Model Context Mgr User Input No Model or Context data update UI Display Display UI Processed Data Get Processed Data Get Data
  • 10.
    Input - ModelData Update Input processing Input Source View Controller Input processing Source View Model Data Model Context Mgr 1: Update Model Data User Input If no model data update, go to Refresh Once step or External Input
  • 11.
    Data Model Update Input processing Input SourceView Controller Input processing Target View Models Data Model Context Mgr Update Model Data Process Data Process / Store / Background + Network Activity Processed Data Source View Model Push Data? - Le problème du target VM est: Est-ce que mes processed data sont valides ou non ? L’information nécessaire est une information de validité. - Il manque les données contextuelles associées, et les données complémentaires du data model. Le view model ne va rien faire des données poussées. - Mon approche préfère le “data-polling” au “data-pushing”. Only valid for simple one data = one refresh scenario. - “Premature optimisation is the root of all evil”. Donald Knuth
  • 12.
    Data Pushing If 1data = 1 atomic processing = 1 refresh VM VCDM KVOKVO View Bindingupdate But if architecture evolves and introduce dependency between several data, synchronisation and multiple refresh issues impose refactor toward data pulling (or usage of more complex frameworks) One notification should not mean “this data has changed” but be a CONTRACT from Data Model allowing to read a defined set of consistent variables (= a state) during the current event cycle
  • 13.
    Mixed Data Push- Pulling VM observe either update value or set processing dirty, then perform processing on read. DM VM Context Mgr VM 2: refresh 1: update 3: notify KVO VC 4: read 5: Process on read - Définir des ensembles de données dépendantes - Après qu’une ou plusieurs de ces données ai été mises à jour, déclencher le rafraîchissement de l’ensemble. - Le refactoring de KVO vers “block update” n’est pas naturel, car il passe d’un mécanisme de “push-data” à un mécanisme “pull-data”. - On considère par la suite les données “complexes”
  • 14.
    Data Pulling VM readData Model on VC request DM VM Processing Context Mgr VM 2: refresh 1: update 3: notify 5: read & process VC 4: read Chosen option: simple calls without complexity or performance penalty
  • 15.
    Context Data +Refresh Step Input processing Input Source View Controller Input processing Source View Model Data Model Context Mgr 1: Set Model Data Refresh Once 2: Set Context Data / Notify Trigger only 1 notification per UI Refresh ! Progress
  • 16.
    Notify UI Display Target View ControllerTarget View Model Data Model Context Mgr Refresh Notification Refresh UI Notify Hierarchical, ordered refresh of the targets paired VM/VC, depending on Context Data and Model Data dependencies. Processed Data 2: Refresh UI ?
  • 17.
    Context Mgr Updated DataVC1 VC2 VC3 Data 1 x x Data 2 x x Data 3 x Data 4 x func onDataSetXUpdate() { viewModel1.refreshVC1() viewModel2.refreshVC2() } Dependent VC upon data set update Data Set X - Un Data Set est un ensemble de données du data model + context mgr lus par une fonction de refresh. - Le but du context manager est une sorte de KVO étendu, qui appelle la fonction de refresh lorsque le data set a été modifié. - Mon implémentation est statique, hard codée, mais Nicolas Bouilleaud a suggéré de la rendre dynamique, avec un VM qui s’abonne à un data set.
  • 18.
    and Display UI UI Display TargetView Controller Refresh Notification Get UI Processed Data Target View Model Data Model Context MgrProcessed Data 2: Refresh UI Get Context Data Display UI If View Controller exists in the VC hierarchy then refresh its UI Get Model Data
  • 19.
    Full Picture Input processing Input UI Display Target VC Input processing Refresh Notification GetUI Processed Data Target VM Data Model Context Mgr Processed Data Refresh UI Get Data It seems complex (many calls), but it is simple because it is the same pattern for ALL the VC/VM/DM/CM interactions. 1: Set Model Data 2: Set Context Data / Notify Progress Source VC Source VM - Chaque composant a un rôle défini et limité. Les functions sont simples et courtes. - Il dispose des interfaces lui permettant de l’accomplir - On peut gérer / partager le développement à plusieurs. - Le context manager semble être central, mais il a un rôle très macroscopique. Il ne sait pas si les VC sont présents ou non, il ne fait que coordonner les dépendances et le rafraîchissement.
  • 20.
    View Model centralrole Input Management: Convert and Forward input request to Data Model, then Context Manager. Refresh: Forward refresh requests from Context Manager to View Controller, if present. Processing: At View Controller request, process data from data model and context manager into “ready to display” processed data. Clear this data on VC dismiss. Input processing Refresh Notification Processed Data VC View Model VC VC CM DM CM DM CM Only use simple calls. No framework dependency - Possibilité de partager les fonctions Input/Processing dans des shared view model, composants intermédiaires entre Data Model et View Model.
  • 21.
    Conclusion Let’s have ademo: The demo is available on github: MVVM-C https://github.com/xlasne/MVVM-C
  • 22.
    Why not storingthe context data into the view controller hierarchy ? Because each View Controller would then depend strongly on its ancestor chain, making hierarchy change more difficult. Because if the data is not in its direct responding chain, it may be in a VC which is not instantiated. A contextual value shared between to child is thus located in a common ancestor, which has no direct interest in this value, or requires additional VM/VM interactions Because it increases the number of connected/dependent components, hence the number of sequences / tests / bugs. Because it increase the VC size, and VC already have a lot to manage. And for these gains, I am ready to pay the price of replicating the view hierarchy into the dependency management of the Context Mgr.
  • 23.
    Isn’t VC-VM oneto one relationship prone to code repetition, unnecessary proxy to data model ? Yes it is. But it forces you to clearly decouple the application logic from the view controller logic. Data write and read logic is more clearly exposed than hidden inside a view controller. And this extra step is what you would need to do if the data become shared: doing it clean first is not a big work, and it is “final”. And if you want to refactor this logic into a shared view model, the refactoring is easy. And it is like a drug … once you have tasted it, you can not resist to its appealing simplicity.