OSX Complex Application Challenge Architecture

23
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

Transcript of OSX Complex Application Challenge Architecture

Page 1: 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

Page 2: OSX Complex Application Challenge Architecture

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.

Page 3: OSX Complex Application Challenge Architecture

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

Page 4: OSX Complex Application Challenge Architecture

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ō

Page 5: OSX Complex Application Challenge Architecture

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.

Page 6: OSX Complex Application Challenge Architecture

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

Page 7: OSX Complex Application Challenge Architecture

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

Page 8: OSX Complex Application Challenge Architecture

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

Page 9: OSX Complex Application Challenge Architecture

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

Page 10: OSX Complex Application Challenge Architecture

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

Page 11: OSX Complex Application Challenge Architecture

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

Page 12: OSX Complex Application Challenge Architecture

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

Page 13: OSX Complex Application Challenge Architecture

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”

Page 14: OSX Complex Application Challenge Architecture

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

Page 15: OSX Complex Application Challenge Architecture

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

Page 16: OSX Complex Application Challenge Architecture

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

?

Page 17: OSX Complex Application Challenge Architecture

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.

Page 18: OSX Complex Application Challenge Architecture

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

Page 19: OSX Complex Application Challenge Architecture

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.

Page 20: OSX Complex Application Challenge Architecture

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.

Page 21: OSX Complex Application Challenge Architecture

Conclusion

Let’s have a demo:

The demo is available on github: MVVM-C

https://github.com/xlasne/MVVM-C

Page 22: OSX Complex Application Challenge Architecture

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.

Page 23: OSX Complex Application Challenge Architecture

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.