OSX Complex Application Challenge Architecture
-
Upload
cocoaheads-france -
Category
Software
-
view
4.409 -
download
0
Transcript of OSX Complex Application Challenge Architecture
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 ChallengeWhen 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 GoalsMinimise 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 simpleRevenge 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 VC1: 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 ConventionInput 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 & InterfaceApplication
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 OwnershipVC
VM
Shared VM
Data Model
Owns views and non-shared dynamic dataDynamic 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 UIProcessed
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 PushingIf 1 data = 1 atomic processing = 1 refresh
VM VCDMKVOKVO
ViewBindingupdate
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 - PullingVM observe either update value or set processing
dirty, then perform processing on read.
DM VM
Context Mgr
VM
2: refresh
1: update
3: notify
KVOVC
4: read5: 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 PullingVM read Data Model on VC request
DM VM Processing
Context Mgr
VM
2: refresh
1: update
3: notify
5: read & process
VC4: 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 Once2: 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 MgrUpdated Data VC1 VC2 VC3
Data 1 x xData 2 x xData 3 xData 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
DataTarget 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 PictureInput
processingInput
UI Display
Target VC
Input processing
Refresh Notification
Get UI Processed
Data Target VM
Data Model
Context MgrProcessed 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 roleInput 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
CMDM
CM
DMCM
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.