Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
@Xebiconfr #Xebicon18 @pjechris
Build the future
Architecturer son application mobile
sur la durée
Jean-Christophe Pastant...
@Xebiconfr #Xebicon18 @pjechris
Qui suis-je ?
Jean-Christophe Pastant
iOS Developer/Architect
Xebia IT Architects
@pjechri...
@Xebiconfr #Xebicon18 @pjechris
Une architecture n’a pas besoin d’être complexe
pour être réussie
3
@Xebiconfr #Xebicon18 @pjechris
Architecturer ?
4
@Xebiconfr #Xebicon18 @pjechris
Bonne architecture ?
5
@Xebiconfr #Xebicon18 @pjechris
Bonne architecture ?
● Orientée vers le besoin fonctionnel
● Construite pour le présent (P...
@Xebiconfr #Xebicon18 @pjechris
Architecture pérenne ?
7
@Xebiconfr #Xebicon18 @pjechris
Architecture pérenne ?
● Robuste dans le temps (petites modifications)
● Évolutive dans le...
@Xebiconfr #Xebicon18 @pjechris
Architecturer ?
Une bonne architecture pérenne se doit d’être :
● Simple
KEEP IT SIMPLE, S...
@Xebiconfr #Xebicon18 @pjechris
Structurer
en couches
Keep it simple, You ain’t gonna need it, Domain Driven Design, Separ...
@Xebiconfr #Xebicon18 @pjechris
Structurer en couches permet de :
● Séparer les responsabilités
○ Affichage
○ Appels résea...
@Xebiconfr #Xebicon18 @pjechris
Structurer en couches
Données
Métier
UI
DB WS Cache
login
logout
checkout
addBook
Layout i...
@Xebiconfr #Xebicon18 @pjechris
Structurer en couches
Données
Métier
UI
DB WS Cache
login
logout
checkout
addBook
Layout i...
@Xebiconfr #Xebicon18 @pjechris
Modèles métier
14
Keep it simple, Domain Driven Design
@Xebiconfr #Xebicon18 @pjechris
Exemple
● Pouvoir ajouter des livres dans un panier
● Afficher le panier
● Afficher le pri...
@Xebiconfr #Xebicon18 @pjechris
Modèles métier
struct Book {
let id: Int
let isbn: String
let title: String
let price: Flo...
@Xebiconfr #Xebicon18 @pjechris
Modèles métier
import ReactiveKit
struct Cart {
private(set) var books: [Book]
var total: ...
@Xebiconfr #Xebicon18 @pjechris
Modèles métier
import ReactiveKit
struct Cart {
private(set) var books: [Book]
var total: ...
@Xebiconfr #Xebicon18 @pjechris
Modèles métier
struct Cart {
private(set) var books: [Book]
var total: Float
mutating func...
@Xebiconfr #Xebicon18 @pjechris
Modèles métier
● Notion centrale du métier/fonctionnel dans l’application
● Donne du sens ...
@Xebiconfr #Xebicon18 @pjechris
UI
“Dumb components”
Keep it simple, Domain Driven Design, Open/Closed principle
@Xebiconfr #Xebicon18 @pjechris
Exemple
Application Fnac (Android)
CartItemView
priceLabel
title
22
@Xebiconfr #Xebicon18 @pjechris
Exemple
Application Fnac (Android)
CartItemView
23
CartRecapView
@Xebiconfr #Xebicon18 @pjechris
UI
class CartItemView: UIView {
var priceLabel: UILabel!
var title: UILabel!
func configur...
@Xebiconfr #Xebicon18 @pjechris
UI
class CartItemView: UIView {
var priceLabel: UILabel!
var title: UILabel!
func configur...
@Xebiconfr #Xebicon18 @pjechris
UI
class CartItemView: UIView {
var priceLabel: UILabel!
var title: UILabel!
func configur...
@Xebiconfr #Xebicon18 @pjechris
UI
class CartItemView: UIView {
var priceLabel: UILabel!
var title: UILabel!
func configur...
@Xebiconfr #Xebicon18 @pjechris
UI
struct PriceComponent {
func bind(price: Float, label: UILabel) {
}
}
28
@Xebiconfr #Xebicon18 @pjechris
UI
struct PriceComponent {
func bind(price: Float, label: UILabel) {
label.text = stringif...
@Xebiconfr #Xebicon18 @pjechris
UI
struct PriceComponent {
func bind(price: Float, label: UILabel) {
label.text = stringif...
@Xebiconfr #Xebicon18 @pjechris
UI
class CartItemView: UIView {
var priceLabel: UILabel!
var title: UILabel!
func configur...
@Xebiconfr #Xebicon18 @pjechris
UI
class CartItemView: UIView {
var priceLabel: UILabel!
var title: UILabel!
func configur...
@Xebiconfr #Xebicon18 @pjechris
UI
● Faire des petits composants UI réutilisables
● Préférer combiner les composants plutô...
@Xebiconfr #Xebicon18 @pjechris
Métier
“Smart components”
Keep it simple, You ain’t gonna need it, Domain Driven Design
@Xebiconfr #Xebicon18 @pjechris
Métier
class CartComponentContext {
// API
private(set) var cart: Variable<Cart>
let check...
@Xebiconfr #Xebicon18 @pjechris
Métier
class CartComponentContext {
// API
private(set) var cart: Variable<Cart>
let check...
@Xebiconfr #Xebicon18 @pjechris
Métier
class CartComponentContext {
// API
private(set) var cart: Variable<Cart>
let check...
@Xebiconfr #Xebicon18 @pjechris
Métier
● Lien entre la couche données et la couche UI
● Définissent les actions / règles m...
@Xebiconfr #Xebicon18 @pjechris
Données
39
Keep it simple, Separation of Concern
@Xebiconfr #Xebicon18 @pjechris
Données
class XebiaWSRepository {
func checkout(cart: Cart, success: (Void) -> (), failure...
@Xebiconfr #Xebicon18 @pjechris
Données
class XebiaWSRepository {
func checkout(cart: Cart, success: (Void) -> (), failure...
@Xebiconfr #Xebicon18 @pjechris
class XebiaWSRepository {
func checkout(cart: Cart, success: (Void) -> (), failure:
(Error...
@Xebiconfr #Xebicon18 @pjechris
Données
class XebiaWSRepository {
func checkout(cart: Cart) -> Promise<Void> {
}
}
43
@Xebiconfr #Xebicon18 @pjechris
Données
class XebiaWSRepository {
let builder: HttpQueryBuilder<XebiaEndpoint>
func checko...
@Xebiconfr #Xebicon18 @pjechris
Données
class XebiaWSRepository {
let builder: HttpQueryBuilder<XebiaEndpoint>
func checko...
@Xebiconfr #Xebicon18 @pjechris
Données
● Doit être simple mais fiable et lisible
● Stateless
● Mapping 1-to-1 avec les se...
@Xebiconfr #Xebicon18 @pjechris
Providers
47
Separation of Concern
@Xebiconfr #Xebicon18 @pjechris
Providers
class CartProvider {
let httpRepository: XebiaWSRepository
func checkout(cart: C...
@Xebiconfr #Xebicon18 @pjechris
Providers
class CartProvider {
let httpRepository: XebiaWSRepository
let realmRepository: ...
@Xebiconfr #Xebicon18 @pjechris
Providers
class CartComponentContext {
// API
private(set) var cart: Variable<Cart>
let ch...
@Xebiconfr #Xebicon18 @pjechris
Providers
class CartComponentContext {
// API
private(set) var cart: Variable<Cart>
let ch...
@Xebiconfr #Xebicon18 @pjechris
Providers
● Facilite la synchronisation/les accès à plusieurs Repository
● Réduit la logiq...
@Xebiconfr #Xebicon18 @pjechris
Injection de dépendances
53
Keep it simple, Separation of Concern
@Xebiconfr #Xebicon18 @pjechris
Injection de dépendances
class CartComponentContext {
// API
private(set) var cart: Variab...
@Xebiconfr #Xebicon18 @pjechris
Injection de dépendances
class CartComponentContext {
// API
private(set) var cart: Variab...
@Xebiconfr #Xebicon18 @pjechris
Injection de dépendances
● Aide à garder les couches indépendantes
● Limite la connaissanc...
@Xebiconfr #Xebicon18 @pjechris
Tests unitaires
57
@Xebiconfr #Xebicon18 @pjechris
Tests unitaires
Tester doit être simple
Si :
● Vous écrivez trop de lignes pour un test (+...
@Xebiconfr #Xebicon18 @pjechris
We did it!
59
@Xebiconfr #Xebicon18 @pjechris
We did it!
● Simple
Petites classes, code réutilisable, séparation des rôles
● Pragmatique...
@Xebiconfr #Xebicon18 @pjechris
MERCI
Questions ?
Upcoming SlideShare
Loading in …5
×

XebiCon'18 - Architecturer son application mobile pour la durabilité

36 views

Published on

Vous avez l’impression que vous devez jeter la moitié de votre code tous les 6 mois ? Plus le temps passe, moins votre application mobile est maintenable ?

Injection de dépendances, routage ou gestion des environnements, venez découvrir nos conseils pour vous aider à réaliser des applications mobiles évolutives conçues pour durer dans le temps.

Par Jean-Christophe Pastant, Mobile Engineer chez Xebia

Toutes les informations sur xebicon.fr

Published in: Technology
  • Be the first to comment

  • Be the first to like this

XebiCon'18 - Architecturer son application mobile pour la durabilité

  1. 1. @Xebiconfr #Xebicon18 @pjechris Build the future Architecturer son application mobile sur la durée Jean-Christophe Pastant 1
  2. 2. @Xebiconfr #Xebicon18 @pjechris Qui suis-je ? Jean-Christophe Pastant iOS Developer/Architect Xebia IT Architects @pjechris 2
  3. 3. @Xebiconfr #Xebicon18 @pjechris Une architecture n’a pas besoin d’être complexe pour être réussie 3
  4. 4. @Xebiconfr #Xebicon18 @pjechris Architecturer ? 4
  5. 5. @Xebiconfr #Xebicon18 @pjechris Bonne architecture ? 5
  6. 6. @Xebiconfr #Xebicon18 @pjechris Bonne architecture ? ● Orientée vers le besoin fonctionnel ● Construite pour le présent (Pragmatique) ● Lisible (Simple) ● Cohérente (Simple, Pragmatique) 6
  7. 7. @Xebiconfr #Xebicon18 @pjechris Architecture pérenne ? 7
  8. 8. @Xebiconfr #Xebicon18 @pjechris Architecture pérenne ? ● Robuste dans le temps (petites modifications) ● Évolutive dans le temps (grosses modifications) 8
  9. 9. @Xebiconfr #Xebicon18 @pjechris Architecturer ? Une bonne architecture pérenne se doit d’être : ● Simple KEEP IT SIMPLE, STUPID ● Pragmatique YOU AIN’T GONNA NEED IT ● Fonctionnelle/métier DOMAIN DRIVEN DESIGN ● Modifiable/évolutive SEPARATION OF CONCERN, OPEN/CLOSED PRINCIPLE 9
  10. 10. @Xebiconfr #Xebicon18 @pjechris Structurer en couches Keep it simple, You ain’t gonna need it, Domain Driven Design, Separation of Concern
  11. 11. @Xebiconfr #Xebicon18 @pjechris Structurer en couches permet de : ● Séparer les responsabilités ○ Affichage ○ Appels réseaux ○ …. ● Réfléchir quel rôle (couche) joue une classe ● Être plus ouvert aux changements ○ On est agnostique des couches supérieures Structurer en couches 11
  12. 12. @Xebiconfr #Xebicon18 @pjechris Structurer en couches Données Métier UI DB WS Cache login logout checkout addBook Layout i18n Formatting 12
  13. 13. @Xebiconfr #Xebicon18 @pjechris Structurer en couches Données Métier UI DB WS Cache login logout checkout addBook Layout i18n Formatting Modèles métier 13
  14. 14. @Xebiconfr #Xebicon18 @pjechris Modèles métier 14 Keep it simple, Domain Driven Design
  15. 15. @Xebiconfr #Xebicon18 @pjechris Exemple ● Pouvoir ajouter des livres dans un panier ● Afficher le panier ● Afficher le prix total du panier source : application Fnac (Android) 15
  16. 16. @Xebiconfr #Xebicon18 @pjechris Modèles métier struct Book { let id: Int let isbn: String let title: String let price: Float } 16
  17. 17. @Xebiconfr #Xebicon18 @pjechris Modèles métier import ReactiveKit struct Cart { private(set) var books: [Book] var total: Observable<Float> mutating func add(book: Book) { } mutating func remove(index: Int) { } } 17
  18. 18. @Xebiconfr #Xebicon18 @pjechris Modèles métier import ReactiveKit struct Cart { private(set) var books: [Book] var total: Float mutating func add(book: Book) { } mutating func remove(index: Int) { } } 18
  19. 19. @Xebiconfr #Xebicon18 @pjechris Modèles métier struct Cart { private(set) var books: [Book] var total: Float mutating func add(book: Book) { } mutating func remove(book: Book) { } } 19
  20. 20. @Xebiconfr #Xebicon18 @pjechris Modèles métier ● Notion centrale du métier/fonctionnel dans l’application ● Donne du sens dans la communication entre les couches ● Doit être orienté métier ● Objets simples ○ Immutables ○ Pas de réseau, pas d’asynchrone, etc... 20
  21. 21. @Xebiconfr #Xebicon18 @pjechris UI “Dumb components” Keep it simple, Domain Driven Design, Open/Closed principle
  22. 22. @Xebiconfr #Xebicon18 @pjechris Exemple Application Fnac (Android) CartItemView priceLabel title 22
  23. 23. @Xebiconfr #Xebicon18 @pjechris Exemple Application Fnac (Android) CartItemView 23 CartRecapView
  24. 24. @Xebiconfr #Xebicon18 @pjechris UI class CartItemView: UIView { var priceLabel: UILabel! var title: UILabel! func configure(title: String, price: String) { priceLabel.text = price title.text = title } } 24
  25. 25. @Xebiconfr #Xebicon18 @pjechris UI class CartItemView: UIView { var priceLabel: UILabel! var title: UILabel! func configure(title: String, price: String) { priceLabel.text = price title.text = title } } class CartRecapView: UIView { var priceLabel: UILabel! func configure(price: String) { priceLabel.text = price } } 25
  26. 26. @Xebiconfr #Xebicon18 @pjechris UI class CartItemView: UIView { var priceLabel: UILabel! var title: UILabel! func configure(book: Book) { priceLabel.text = price title.text = title } } class CartRecapView: UIView { var priceLabel: UILabel! func configure(cart: Cart) { priceLabel.text = price } } 26
  27. 27. @Xebiconfr #Xebicon18 @pjechris UI class CartItemView: UIView { var priceLabel: UILabel! var title: UILabel! func configure(book: Book) { priceLabel.text = stringify(book.price, .currency) title.text = book.title.uppercased } } class CartRecapView: UIView { var priceLabel: UILabel! func configure(cart: Cart) { priceLabel.text = stringify(cart.total, .currency) } } 27
  28. 28. @Xebiconfr #Xebicon18 @pjechris UI struct PriceComponent { func bind(price: Float, label: UILabel) { } } 28
  29. 29. @Xebiconfr #Xebicon18 @pjechris UI struct PriceComponent { func bind(price: Float, label: UILabel) { label.text = stringify(price, style: .currency) } } 29
  30. 30. @Xebiconfr #Xebicon18 @pjechris UI struct PriceComponent { func bind(price: Float, label: UILabel) { label.text = stringify(price, style: .currency) } } struct TitleComponent { func bind(title: String, label: UILabel) { label.text = title.capitalize } } 30
  31. 31. @Xebiconfr #Xebicon18 @pjechris UI class CartItemView: UIView { var priceLabel: UILabel! var title: UILabel! func configure(book: Book) { priceLabel.text = stringify(book.price, .currency) title.text = book.title.uppercased } } class CartRecapView: UIView { var priceLabel: UILabel! func configure(cart: Cart) { priceLabel.text = stringify(cart.total, .currency) } } 31
  32. 32. @Xebiconfr #Xebicon18 @pjechris UI class CartItemView: UIView { var priceLabel: UILabel! var title: UILabel! func configure(book: Book) { priceLabel.bind(book.price, with: PriceComponent.self) title.bind(book.title, with: TitleComponent.self) } } class CartRecapView: UIView { var priceLabel: UILabel! func configure(cart: Cart) { priceLabel.bind(cart.total, with: PriceComponent.self) } } 32
  33. 33. @Xebiconfr #Xebicon18 @pjechris UI ● Faire des petits composants UI réutilisables ● Préférer combiner les composants plutôt que l’héritage ● Exposer une API métier 33
  34. 34. @Xebiconfr #Xebicon18 @pjechris Métier “Smart components” Keep it simple, You ain’t gonna need it, Domain Driven Design
  35. 35. @Xebiconfr #Xebicon18 @pjechris Métier class CartComponentContext { // API private(set) var cart: Variable<Cart> let checkoutAction: Action } 35
  36. 36. @Xebiconfr #Xebicon18 @pjechris Métier class CartComponentContext { // API private(set) var cart: Variable<Cart> let checkoutAction: Action private let httpRepository: XebiaWSRepository init(cart: Cart) { self.cart = cart self.httpRepository = XebiaWSRepository(...) } } 36
  37. 37. @Xebiconfr #Xebicon18 @pjechris Métier class CartComponentContext { // API private(set) var cart: Variable<Cart> let checkoutAction: Action private let httpRepository: XebiaWSRepository init(cart: Cart) { self.cart = cart self.httpRepository = XebiaWSRepository(...) self.checkoutAction = Action( enabledIf: { !cart.isEmpty }, factory: { httpRepository.checkout(self.cart) }) } } 37
  38. 38. @Xebiconfr #Xebicon18 @pjechris Métier ● Lien entre la couche données et la couche UI ● Définissent les actions / règles métier ○ Passer une commande seulement si le panier n’est pas vide ○ … ● Font les appels réseaux ● Gérent les états ● Exposent une API métier 38
  39. 39. @Xebiconfr #Xebicon18 @pjechris Données 39 Keep it simple, Separation of Concern
  40. 40. @Xebiconfr #Xebicon18 @pjechris Données class XebiaWSRepository { func checkout(cart: Cart, success: (Void) -> (), failure: (Error) -> ()) { } 40
  41. 41. @Xebiconfr #Xebicon18 @pjechris Données class XebiaWSRepository { func checkout(cart: Cart, success: (Void) -> (), failure: (Error) -> ()) { let url = URL(string: “…”) SessionManager.default.request(url).responseJSON { } } 41
  42. 42. @Xebiconfr #Xebicon18 @pjechris class XebiaWSRepository { func checkout(cart: Cart, success: (Void) -> (), failure: (Error) -> ()) { let url = URL(string: “…”) SessionManager.default.request(url).responseJSON { let json = JSON(data: $0.jsonData) do { let books = try JSONDecoder().decode([Book].self, from: jsonData) success(books) } catch let error { failure(error) } } } 42
  43. 43. @Xebiconfr #Xebicon18 @pjechris Données class XebiaWSRepository { func checkout(cart: Cart) -> Promise<Void> { } } 43
  44. 44. @Xebiconfr #Xebicon18 @pjechris Données class XebiaWSRepository { let builder: HttpQueryBuilder<XebiaEndpoint> func checkout(cart: Cart) -> Promise<Void> { return builder.query(.checkout(cart)).request() } } 44
  45. 45. @Xebiconfr #Xebicon18 @pjechris Données class XebiaWSRepository { let builder: HttpQueryBuilder<XebiaEndpoint> func checkout(cart: Cart) -> Promise<Void> { return builder.query(.checkout(cart)).request() } func findBooks() -> Promise<[Book]> { return builder.query(.books).request() } } 45
  46. 46. @Xebiconfr #Xebicon18 @pjechris Données ● Doit être simple mais fiable et lisible ● Stateless ● Mapping 1-to-1 avec les services fournies ○ 1 classe = 1 service (DB, WS, …) ○ 1 méthode = 1 API 46
  47. 47. @Xebiconfr #Xebicon18 @pjechris Providers 47 Separation of Concern
  48. 48. @Xebiconfr #Xebicon18 @pjechris Providers class CartProvider { let httpRepository: XebiaWSRepository func checkout(cart: Cart) -> Promise<Void> { return httpRepository .checkout(cart) } } 48
  49. 49. @Xebiconfr #Xebicon18 @pjechris Providers class CartProvider { let httpRepository: XebiaWSRepository let realmRepository: RealmRepository func checkout(cart: Cart) -> Promise<Void> { return httpRepository .checkout(cart) .then { realmRepository.savePurchasedCart($0) } } } 49
  50. 50. @Xebiconfr #Xebicon18 @pjechris Providers class CartComponentContext { // API private(set) var cart: Variable<Cart> let checkoutAction: Action private let httpRepository: XebiaWSRepository init(cart: Cart) { self.cart = cart self.httpRepository = XebiaWSRepository(...) self.checkoutAction = Action( enabledIf: { !cart.isEmpty }, factory: { httpRepository.checkout(self.cart) }) } } 50
  51. 51. @Xebiconfr #Xebicon18 @pjechris Providers class CartComponentContext { // API private(set) var cart: Variable<Cart> let checkoutAction: Action private let cartProvider: CartProvider init(cart: Cart) { self.cart = cart self.cartProvider = CartProvider(...) self.checkoutAction = Action( enabledIf: { !cart.isEmpty }, factory: { cartProvider.checkout(cart: self.cart) }) } } 51
  52. 52. @Xebiconfr #Xebicon18 @pjechris Providers ● Facilite la synchronisation/les accès à plusieurs Repository ● Réduit la logique dans les Smart Components ● 1 classe par objet métier (BookProvider, CartProvider, …) 52
  53. 53. @Xebiconfr #Xebicon18 @pjechris Injection de dépendances 53 Keep it simple, Separation of Concern
  54. 54. @Xebiconfr #Xebicon18 @pjechris Injection de dépendances class CartComponentContext { // API private(set) var cart: Variable<Cart> let checkoutAction: Action private let cartProvider: CartProvider init(cart: Cart) { self.cart = cart self.cartProvider = CartProvider(...) self.checkoutAction = Action( enabledIf: { !cart.isEmpty }, factory: { cartProvider.checkout(cart: self.cart) }) } } 54
  55. 55. @Xebiconfr #Xebicon18 @pjechris Injection de dépendances class CartComponentContext { // API private(set) var cart: Variable<Cart> let checkoutAction: Action private let cartProvider: CartProvider init(cart: Cart, cartProvider: CartProvider) { self.cart = cart self.cartProvider = cartProvider self.checkoutAction = Action( enabledIf: { !cart.isEmpty }, factory: { cartProvider.checkout(cart: self.cart) }) } } 55
  56. 56. @Xebiconfr #Xebicon18 @pjechris Injection de dépendances ● Aide à garder les couches indépendantes ● Limite la connaissance de chaque objet au strict minimum ● Permet de tester plus facilement 56
  57. 57. @Xebiconfr #Xebicon18 @pjechris Tests unitaires 57
  58. 58. @Xebiconfr #Xebicon18 @pjechris Tests unitaires Tester doit être simple Si : ● Vous écrivez trop de lignes pour un test (+ 10 lignes) ● C’est trop compliqué de tester malgré tous vos protocoles/interfaces Revoyez votre architecture ! 58
  59. 59. @Xebiconfr #Xebicon18 @pjechris We did it! 59
  60. 60. @Xebiconfr #Xebicon18 @pjechris We did it! ● Simple Petites classes, code réutilisable, séparation des rôles ● Pragmatique Code incrémental, isolation des rôles ● Fonctionnelle/métier APIs orientées modèles métier ● Modifiable/évolutive Structure en couches, injection de dépendances, principes SOLID 60
  61. 61. @Xebiconfr #Xebicon18 @pjechris MERCI Questions ?

×