Ah, lâarchitecture dâune app iOS.
Vaste sujet qui me prĂ©occupe depuis que jâai commencĂ© Ă crĂ©er des apps iOS en 2010 (sortie du premier iPad, de lâiPhone 4 et dâiOS 4 !).
Ă chaque nouvelle app se pose cette question fatidique : quelle archi mettre en place ?
AprĂšs tout, câest un choix important, non ? Une fois lâarchitecture choisie, impossible de revenir en arriĂšre, nâest-ce pas ?
Mais dites-moi, câest quoi une architecture ?
Architecture : définition
Prenons la définition du Larousse :
Organisation des divers Ă©lĂ©ments constitutifs dâun systĂšme informatique, en vue dâoptimiser la conception de lâensemble pour un usage dĂ©terminĂ©.
Il y a la notion dâ organisation ; il sâagit lĂ dâorganiser son code, ses modules, ses frameworks.
Lâobjectif est dâ optimiser la conception de lâensemble ; il faut prendre du recul sur tout son code afin de lâoptimiser.
Dans le but de rĂ©pondre Ă un usage dĂ©terminĂ© ; cette notion dâusage, je lâinterprĂšte dâun point de vue fonctionnel.
Maintenant que nous sommes dâaccord sur la dĂ©finition ; quelles sont les caractĂ©ristiques dâune bonne architecture ?
Une bonne architecture
Je vais reprendre chacune des notions et tenter de déterminer les caractéristiques que je juge en adéquation avec la définition.
Pour rappel, il sâagit de limiter la rĂ©flexion au code dâune app iOS. (MĂȘme si, en rĂ©alitĂ©, cette rĂ©flexion est applicable Ă tous logiciels.)
Organisation
Comment déterminer que le code de mon app iOS est bien organisé?
Primo, je trouve rapidement ce que je cherche.
Ex: je dois modifier lâĂ©cran de connexion, il doit bien exister un LoginViewController
ou quelque chose comme ça.
Deuxio, je ne me perds pas en cours de route.
Contre-Ex: le LoginViewController
fait quoi exactement ? Dans viewDidLoad
: il configure des singletons (wtf?), modifie des contraintes (wtf??), fait un appel serveur (wtf???) ; il appelle des mĂ©thodes sur une super classe pourâŠje suis perdu je ne comprends plus rien !
Tertio, les dépendances ne partent pas dans tous les sens.
Contre-Ex: LoginViewController
dépend de ClientAPI
qui dépend de Configuration
qui dépend de ClientAPI
, wtf?
Optimiser la conception de lâensemble
Comment déterminer que la conception est optimisée ?
Primo, je comprends facilement ce que le code fait.
Ex: LoginViewController
a une méthode onLoginButtonTapped()
qui appelle behavior.login()
.
Contre-Ex: LoginViewController
a une méthode buttonTapped(_ sender: NSObject)
qui teste si sender == button1
et qui fait plein de trucs sur 200 lignes avec un niveau dâimbrication au-delĂ de toute raison et problablement un appel API cachĂ© au milieu ?
Deuxio, je retrouve les termes métiers dans le code.
Ex: quand je parle avec les utilisateurs et utilisatrices, ils Ă©voquent des Recettes et des IngrĂ©dients ; dans le code jâai bien les notions de Recipe
et dâIngredient
.
Tertio, jâarrive Ă changer facilement le code.
Contre-Ex: je modifie le login pour amĂ©liorer lâexpĂ©rience utilisateur, je livre, et jâai 36 nouveaux bugs Ă des endroits improbables, whaaaat?
Un usage déterminé
Comment déterminer que la conception répond à un usage précis ?
Primo, il nây a pas de code au cas oĂč.
Contre-Ex: je dois afficher des informations textuelles dans la v1. Je crée un InformationsBuilder
qui accepte une dépendance implémentant le protocole InformationsStrategy
et je crée une classe TextualInformationsStrategy
qui implémente ce protocole.
Autre contre-ex: je vais crĂ©er une DSL de configuration de style, au cas oĂč je dois changer le thĂšme de lâapp un jour.
Deuxio, il nây a vraiment pas de code au cas oĂč !
Il mâest dĂ©jĂ arrivĂ© de complexifier le code en essayant dâanticiper les besoins. Câest tentant et motivant de crĂ©er du code rĂ©utilisable. Mais dans 95% des cas câest too much.
Mieux vaut dupliquer pour trouver la bonne abstraction que créer une mauvaise abstraction.
Je préfÚre dupliquer une ou deux fois, puis prendre du recul pour trouver comment factoriser le code, afin de créer des abstractions qui ont une réelle utilité et un véritable sens.
Une bonne conception
Une bonne architecture veut donc avant tout dire une bonne conception.
Une bonne conception respecte les critĂšres que jâai listĂ©s ci-dessus et que je rappelle ici :
- je trouve rapidement ce que je cherche,
- je ne me perds pas en cours de route,
- les dépendances ne partent pas dans tous les sens,
- je comprends facilement ce que le code fait,
- je retrouve les termes métiers dans le code,
- jâarrive Ă changer facilement le code,
- il nây a pas de code au cas oĂč,
- le code répond à un usage déterminé.
Je ne vais pas vous le cacher, avoir une architecture qui remplit tous ces critĂšres est trĂšs difficile.
Il faut dĂ©jĂ rĂ©ussir Ă bien comprendre le besoin, Ă dĂ©terminer prĂ©cisĂ©ment lâusage.
Il ne faut pas anticiper des demandes qui nâarriveront peut-ĂȘtre jamais.
Il faut rendre son code compréhensible par les autres humains qui vont le lire (et nous ne sommes pas tous égaux à ce niveau).
Il faut faire face aux particularitĂ©s des frameworks et librairies quâon utilise.
Autant vous dire quâavoir juste du premier coup relĂšve du miracle !
Faire Ă©merger la conception
Mais si je nâarrive pas Ă avoir juste du premier coupâŠcela veut dire que je vais devoir changer ma conception en cours de route, la faire Ă©voluer ?
Exactement ! Câest ce que jâappelle faire Ă©merger la conception.
Oh ! Et du coup faire Ă©merger la conception revient Ă crĂ©erâŠ
Une architecture Ă©mergente !
Je pars de deux hypothĂšses pour justifier le fait de faire Ă©merger lâarchitecture, plutĂŽt que de la figer dans le marbre Ă lâavance.
- Il est impossible dâimaginer la bonne conception du premier coup.
- Le besoin change.
Jâai dĂ©jĂ dĂ©taillĂ© le premier point ci-dessus, passons au second.
Le besoin change
Si seulement les utilisateurs, utilisatrices, clientes & clients arrĂȘtaient de changer tout le temps dâavis ! Cela serait beaucoup plus simple ! Nous aurions un cahier des charges figĂ© et des spĂ©cifications fonctionnelles figĂ©es. Il nous serait alors si simple de concevoir une app qui rĂ©ponde exactement Ă ce qui est demandĂ©. Nous pourrions prendre le temps de bien concevoir, de faire de beaux diagrammes. Puis nous livrerions une app bien conçue et nous passerions Ă la suivante.
Le rĂȘve quoi !
Vraiment ?
Nous savons que ce nâest jamais le cas. Qui a dĂ©jĂ vĂ©cu cette situation, honnĂȘtement ?
Le besoin change !
Pourquoi change-t-il ?
Car crĂ©er un logiciel est principalement un travail de communication, de comprĂ©hension et dâempathie.
Entre ce que lâutilisateur a en tĂȘte, ce quâil explique, ce que la dĂ©veloppeuse comprend et ce quâelle exprime par code ; les risques de mauvaise interprĂ©tation sont lĂ©gions ! (Ajoutez quelques intermĂ©diaires entre les deux personnes et vous multiplierez ces risques. Coucou les Product Owner & Proxy Product Owner & Proxy Proxy ProxyâŠ)
Ce nâest pas forcĂ©ment le besoin rĂ©el qui change, câest notre comprĂ©hension qui Ă©volue ! Il nous arrive (souvent) de mal comprendre le besoin rĂ©el. Au fur et Ă mesure que nous crĂ©ons et livrons le logiciel, nous apprenons des feedbacks ! Et nous devons reflĂ©ter cette comprĂ©hension dans notre code.
Conclusion
Bonne architecture veut dire bonne conception.
Avoir une conception juste du premier coup est impossible car le besoin change, notre compréhension de celui-ci change.
Il faut donc ĂȘtre capable de faire Ă©voluer cette conception, de la faire Ă©merger.
Pour cela, nous devons nous assurer que nous ne cassons rien au passage.
Et pour ne rien casserâŠon fait comment ?
Je vous en parle dans le prochain article. đ