Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Effective Multiplatform Architecture

Effective Multiplatform Architecture

Building multiplatform apps requires thinking about architecture a little differently than their single platform counterparts. Individually, each platform has their own norms and customs, filled with M's and V's and Promises, but they aren't alway a good fit to be shared with others. Often they have coupled relationships with the platforms for which they were designed that don't play nicely with others.

Over the past several months I've been migrating the Chicago Roboto app from a Kotlin first Android app to a multiplatform app, and have learned a lot about things that work well for one platform but not another, and how to make those feel a bit more native.

As I share the things I've learned with you, we'll discuss:

• Where to start the multiplatform conversion.
• Adapting native libraries
• Communicating between shared Kotlin code and native platforms
• Designing systems that non-Kotlin developers can get behind.

Ryan Harter

August 27, 2019
Tweet

More Decks by Ryan Harter

Other Decks in Programming

Transcript

  1. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  2. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  3. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  4. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    Account Feature Library Feature Social Repo Content Repo Activity API Content API App App App
  5. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    Account Feature Library Feature Social Repo Content Repo Activity API Content API App App App
  6. #DCNYC19 @rharter Multiplatform App App Auth API Database Login Feature

    User Repo Account Feature Library Feature Social Repo Content Repo Activity API Content API
  7. #DCNYC19 @rharter Modularize • Easier to migrate one module at

    a time • “clean” architecture is important (MVI) •
  8. #DCNYC19 @rharter Modularize • Easier to migrate one module at

    a time • “clean” architecture is important (MVI) • Inversion of Control (Dependency Injection) •
  9. #DCNYC19 @rharter Modularize • Easier to migrate one module at

    a time • “clean” architecture is important (MVI) • Inversion of Control (Dependency Injection) • Minimal platform dependencies •
  10. #DCNYC19 @rharter Modularize • Easier to migrate one module at

    a time • “clean” architecture is important (MVI) • Inversion of Control (Dependency Injection) • Minimal platform dependencies • Limit to View and Repo dependencies •
  11. #DCNYC19 @rharter Modularize • Easier to migrate one module at

    a time • “clean” architecture is important (MVI) • Inversion of Control (Dependency Injection) • Minimal platform dependencies • Limit to View and Repo dependencies • LiveData
  12. #DCNYC19 @rharter Modularize • Easier to migrate one module at

    a time • “clean” architecture is important (MVI) • Inversion of Control (Dependency Injection) • Minimal platform dependencies • Limit to View and Repo dependencies • LiveData
  13. #DCNYC19 @rharter Modularize Auth API Database Login Feature User Repo

    App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  14. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  15. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    Account Feature Library Feature Social Repo Content Repo Activity API Content API App Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  16. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo Account Feature Library Feature Social Repo Content Repo Activity API Content API App
  17. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    App Account Feature Library Feature Social Repo Content Repo Activity API Content API Multiple Similar Apps Single App Multiple Different Apps App Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  18. #DCNYC19 @rharter Single App Auth API Database Login Feature User

    Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  19. #DCNYC19 @rharter Single App Auth API Database Login Feature User

    Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API • All functionality needs to be replicated • Can start anywhere • Same approach as new app
  20. #DCNYC19 @rharter Single App Auth API Database Login Feature User

    Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  21. #DCNYC19 @rharter Single App Auth API Database Login Feature User

    Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  22. #DCNYC19 @rharter Single App Auth API Database Login Feature User

    Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API App
  23. #DCNYC19 @rharter Single App Auth API Database Login Feature User

    Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API App
  24. #DCNYC19 @rharter Single App Auth API Database Login Feature User

    Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API App
  25. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    App Account Feature Library Feature Social Repo Content Repo Activity API Content API Multiple Similar Apps Single App Multiple Different Apps Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API App Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API App Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  26. #DCNYC19 @rharter Multiple Different Apps • Similar to modularization strategy

    • Convert to MPP as you modularize • Pick a feature to convert • Start from the bottom App Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  27. #DCNYC19 @rharter Multiple Different Apps App Auth API Database Login

    Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  28. #DCNYC19 @rharter Multiple Different Apps App Auth API Database Login

    Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  29. #DCNYC19 @rharter Multiple Different Apps App Auth API Database Login

    Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  30. #DCNYC19 @rharter Multiple Different Apps App Auth API Database Login

    Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  31. #DCNYC19 @rharter Multiple Different Apps App Auth API Database Login

    Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  32. #DCNYC19 @rharter Multiple Different Apps App Auth API Database Login

    Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  33. #DCNYC19 @rharter Multiple Different Apps App Auth API Database Login

    Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  34. #DCNYC19 @rharter Multiple Different Apps App Auth API Database Login

    Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  35. #DCNYC19 @rharter Multiple Different Apps App Auth API Database Login

    Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  36. #DCNYC19 @rharter Auth API Database Login Feature User Repo App

    Account Feature Library Feature Social Repo Content Repo Activity API Content API App Multiple Different Apps
  37. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    App Account Feature Library Feature Social Repo Content Repo Activity API Content API Multiple Similar Apps Single App Multiple Different Apps App Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  38. #DCNYC19 @rharter Multiple Similar Apps • Multiple Approaches • New

    Feature • Vertical Migration • Horizontal Migration • Choice depends on existing arch, teams, etc. • Can be done with many small PRs (i.e. reduce risk) Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API
  39. #DCNYC19 @rharter Auth API Database Login Feature User Repo App

    Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API App Similar Apps - New Features
  40. #DCNYC19 @rharter Database Login Feature User Repo App Account Feature

    Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Similar Apps - New Features Subscription Feature
  41. #DCNYC19 @rharter Database Login Feature User Repo App Account Feature

    Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Similar Apps - New Features Subscription Feature
  42. #DCNYC19 @rharter Database Login Feature User Repo App Account Feature

    Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Similar Apps - New Features Subscription Feature
  43. #DCNYC19 @rharter Subscription Feature Common expect interface UserRepository { suspend

    fun getUser(): User suspend fun updateUser(user: User): User ... }
  44. #DCNYC19 @rharter Subscription Feature Common expect interface UserRepository { suspend

    fun getUser(): User suspend fun updateUser(user: User): User ... }
  45. #DCNYC19 @rharter Subscription Feature Common expect interface UserRepository { suspend

    fun getUser(): User suspend fun updateUser(user: User): User ... } iOS actual typealias UserRepository = backend.UserRepository JVM actual typealias UserRepository = com.example.myApp.backend.UserRepository
  46. #DCNYC19 @rharter Database Login Feature User Repo App Account Feature

    Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Similar Apps - New Features Subscription Feature
  47. #DCNYC19 @rharter Auth API Database Login Feature User Repo App

    Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API App Similar Apps - Vertical
  48. #DCNYC19 @rharter Similar Apps - Vertical Database Login Feature User

    Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API
  49. #DCNYC19 @rharter Auth API Database Login Feature User Repo App

    Account Feature Library Feature Social Repo Content Repo Activity API Content API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Similar Apps - Vertical
  50. #DCNYC19 @rharter Database Login Feature User Repo App Account Feature

    Library Feature Social Repo Content Repo Activity API Content API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Similar Apps - Vertical
  51. #DCNYC19 @rharter Database Login Feature User Repo App Account Feature

    Library Feature Social Repo Content Repo Activity API Content API Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Similar Apps - Vertical
  52. #DCNYC19 @rharter Login Feature User Repo App Account Feature Library

    Feature Social Repo Content Repo Activity API Content API Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Similar Apps - Vertical
  53. #DCNYC19 @rharter User Repo Login Feature App Account Feature Library

    Feature Social Repo Content Repo Activity API Content API Login Feature App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database User Repo Similar Apps - Vertical
  54. #DCNYC19 @rharter Login Feature App Account Feature Library Feature Social

    Repo Content Repo Activity API Content API Login Feature App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database User Repo Similar Apps - Vertical
  55. #DCNYC19 @rharter Login Feature App Account Feature Library Feature Social

    Repo Content Repo Activity API Content API App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database User Repo Login Feature Similar Apps - Vertical
  56. #DCNYC19 @rharter Database Login Feature User Repo App Account Feature

    Library Feature Social Repo Content Repo Activity API Content API Auth API App Similar Apps - Vertical
  57. #DCNYC19 @rharter Auth API Database Login Feature User Repo Account

    Feature Library Feature Social Repo Content Repo Activity API Content API App Auth API Database Login Feature User Repo Account Feature Library Feature Social Repo Content Repo Activity API Content API App Similar Apps - Horizontal
  58. #DCNYC19 @rharter Database Login Feature User Repo Account Feature Library

    Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API App Similar Apps - Horizontal
  59. #DCNYC19 @rharter Database Login Feature User Repo Account Feature Library

    Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API App Similar Apps - Horizontal
  60. #DCNYC19 @rharter Database User Repo Account Feature Library Feature Social

    Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API App Similar Apps - Horizontal
  61. #DCNYC19 @rharter Database User Repo Account Feature Library Feature Social

    Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Library Feature Social Repo Content Repo Activity API Content API Auth API App Similar Apps - Horizontal Account Feature Account Feature Library Feature
  62. #DCNYC19 @rharter Database User Repo Social Repo Content Repo Activity

    API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API App Similar Apps - Horizontal User Repo Social Repo Content Repo
  63. #DCNYC19 @rharter Login Feature User Repo App Account Feature Library

    Feature Social Repo Content Repo App Similar Apps - Horizontal Database Activity API Content API Auth API Database Activity API Content API Auth API Auth API Database Activity API Content API
  64. #DCNYC19 @rharter Database User Repo Account Feature Library Feature Social

    Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API App Similar Apps - Horizontal
  65. #DCNYC19 @rharter Login Feature Feature Layer • Interface with platform

    implementations • Views • Repositories (or Use Cases) • Connected at both front and back • How to observe values?
  66. #DCNYC19 @rharter ⚛ Common package org.reactivestreams.mpp expect interface Subscriber<T> {

    fun onSubscribe(s: Subscription) fun onNext(t: T) fun onError(t: Throwable) fun onComplete() }
  67. #DCNYC19 @rharter ⚛ Common JVM package org.reactivestreams.mpp actual typealias Subscriber<T>

    = org.reactivestreams.Subscriber<T> package org.reactivestreams.mpp expect interface Subscriber<T> { fun onSubscribe(s: Subscription) fun onNext(t: T) fun onError(t: Throwable) fun onComplete() }
  68. #DCNYC19 @rharter ⚛ package org.reactivestreams.mpp actual interface Subscriber<T> { actual

    fun onSubscribe(s: Subscription) actual fun onNext(t: T) actual fun onError(t: Throwable) actual fun onComplete() } Common JVM iOS package org.reactivestreams.mpp actual typealias Subscriber<T> = org.reactivestreams.Subscriber<T> package org.reactivestreams.mpp expect interface Subscriber<T> { fun onSubscribe(s: Subscription) fun onNext(t: T) fun onError(t: Throwable) fun onComplete() }
  69. #DCNYC19 @rharter ⚛ package org.reactivestreams.mpp actual interface Subscriber<T> { actual

    fun onSubscribe(s: Subscription) actual fun onNext(t: T) actual fun onError(t: Throwable) actual fun onComplete() } class BlockSubscriber<T>( private val onNextBlock: (T) -> Unit, private val onErrorBlock: (Throwable) -> Unit, private val onCompleteBlock: () -> Unit ) : Subscriber<T>, Disposable { ... } Common JVM iOS package org.reactivestreams.mpp actual typealias Subscriber<T> = org.reactivestreams.Subscriber<T> package org.reactivestreams.mpp expect interface Subscriber<T> { fun onSubscribe(s: Subscription) fun onNext(t: T) fun onError(t: Throwable) fun onComplete() }
  70. #DCNYC19 @rharter ⚛ package org.reactivestreams.mpp actual interface Subscriber<T> { actual

    fun onSubscribe(s: Subscription) actual fun onNext(t: T) actual fun onError(t: Throwable) actual fun onComplete() } class BlockSubscriber<T>( private val onNextBlock: (T) -> Unit, private val onErrorBlock: (Throwable) -> Unit, private val onCompleteBlock: () -> Unit ) : Subscriber<T>, Disposable { ... } iOS extension Reactive_streams_mppPublisher { func subscribe<T: AnyObject>( onNext onNextBlock: @escaping (T) -> Void, onError onErrorBlock: @escaping (Error) -> Void, onComplete onCompleteBlock: @escaping () -> Void ) -> Disposable { let subscriber = BlockSubscriber<T>( onNextBlock: onNextBlock, onErrorBlock: onErrorBlock, onCompleteBlock: onCompleteBlock ) subscribe(s: subscriber) return subscriber } } iOS - Swift
  71. #DCNYC19 @rharter Login Feature JVM ViewModel Common package com.chicagoroboto.features.shared expect

    interface ViewModel<ViewState : Any> { val viewState: Flow<ViewState> } package com.chicagoroboto.features.shared actual interface ViewModel<ViewState : Any> { actual val viewState: Flow<ViewState> }
  72. #DCNYC19 @rharter Login Feature Common JVM package com.chicagoroboto.features.shared expect interface

    ViewModel<ViewState : Any> { val viewState: Flow<ViewState> } ViewModel package com.chicagoroboto.features.shared actual interface ViewModel<ViewState : Any> { actual val viewState: Flow<ViewState> } iOS package com.chicagoroboto.features.shared actual interface ViewModel<ViewState : Any> { actual val viewState: Flow<ViewState> fun viewState(): Publisher<ViewState> = viewState.asPublisher() } fun viewState(): Publisher<ViewState> = viewState.asPublisher()
  73. #DCNYC19 @rharter Login Feature ViewModel Common JVM package com.chicagoroboto.features.shared expect

    interface ViewModel<ViewState : Any> { val viewState: Flow<ViewState> } package com.chicagoroboto.features.shared actual interface ViewModel<ViewState : Any> { actual val viewState: Flow<ViewState> } iOS package com.chicagoroboto.features.shared actual interface ViewModel<ViewState : Any> { actual val viewState: Flow<ViewState> fun viewState(): Publisher<ViewState> = viewState.asPublisher() } fun viewState(): Publisher<ViewState> = viewState.asPublisher()
  74. #DCNYC19 @rharter Login Feature SessionListViewModel class SessionListViewModel( private val sessionRepo:

    SessionRepository, speakerRepo SpeakerRepository, favoriteRepo: FavoriteRepository ) : ViewModel<SessionListViewState> { private val dateChannel = ConflatedBroadcastChannel<String>() private val sessionFlow: Flow<List<Session>> = dateChannel.asFlow() .flatMapConcat { sessionRepo.sessionsForDate(it) } private val speakerFlow: Flow<List<Speaker>> = speakerRepo.getSpeakers() private val favoriteFlow: Flow<Set<String>> = favoriteRepo.getFavorites() override val viewState = combine(sessionFlow, speakerFlow, favoriteFlow) { sessions, speakers, favorites -> SessionListViewState(date, viewSessions) } }
  75. #DCNYC19 @rharter Login Feature SessionListViewModel override val viewState = combine(sessionFlow,

    speakerFlow, favoriteFlow) { sessions, speakers, favorites -> SessionListViewState(date, viewSessions) } class SessionListViewModel( private val sessionRepo: SessionRepository, speakerRepo SpeakerRepository, favoriteRepo: FavoriteRepository ) : ViewModel<SessionListViewState> { private val dateChannel = ConflatedBroadcastChannel<String>() private val sessionFlow: Flow<List<Session>> = dateChannel.asFlow() .flatMapConcat { sessionRepo.sessionsForDate(it) } private val speakerFlow: Flow<List<Speaker>> = speakerRepo.getSpeakers() private val favoriteFlow: Flow<Set<String>> = favoriteRepo.getFavorites() override val viewState = combine(sessionFlow, speakerFlow, favoriteFlow) { sessions, speakers, favorites -> SessionListViewState(date, viewSessions) } }
  76. #DCNYC19 @rharter iOS App .subscribe( onNext: { [weak self] (state:

    SessionListViewState) in self?.sessions = state.sessions }, onError: { error in NSLog("Failed to load state.", error) }, onComplete: { NSLog("onComplete") } ) class SessionListViewController: UICollectionViewController { override func viewDidLoad() { super.viewDidLoad() disposables <= self.viewModel.viewState() .subscribe( onNext: { [weak self] (state: SessionListViewState) in self?.sessions = state.sessions }, onError: { error in NSLog("Failed to load state.", error) }, onComplete: { NSLog("onComplete") } ) self.viewModel.setDate(date: self.date) } }
  77. #DCNYC19 @rharter iOS App .subscribe( onNext: { [weak self] (state:

    SessionListViewState) in self?.sessions = state.sessions }, onError: { error in NSLog("Failed to load state.", error) }, onComplete: { NSLog("onComplete") } ) class SessionListViewController: UICollectionViewController { override func viewDidLoad() { super.viewDidLoad() disposables <= self.viewModel.viewState() .subscribe( onNext: { [weak self] (state: SessionListViewState) in self?.sessions = state.sessions }, onError: { error in NSLog("Failed to load state.", error) }, onComplete: { NSLog("onComplete") } ) self.viewModel.setDate(date: self.date) } }
  78. #DCNYC19 @rharter Login Feature Common JVM package com.chicagoroboto.features.shared expect interface

    ViewModel<ViewState : Any> { val viewState: Flow<ViewState> } ViewModel package com.chicagoroboto.features.shared actual interface ViewModel<ViewState : Any> { actual val viewState: Flow<ViewState> } iOS package com.chicagoroboto.features.shared actual interface ViewModel<ViewState : Any> { actual val viewState: Flow<ViewState> fun viewState(): Publisher<ViewState> = viewState.asPublisher() } fun viewState(): Publisher<ViewState> = viewState.asPublisher()
  79. #DCNYC19 @rharter Database User Repo Account Feature Library Feature Social

    Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API App Similar Apps - Horizontal
  80. #DCNYC19 @rharter Login Feature User Repo App Account Feature Library

    Feature Social Repo Content Repo App Similar Apps - Horizontal Auth API Database Activity API Content API
  81. #DCNYC19 @rharter Multiplatform Auth API Database Login Feature User Repo

    App Account Feature Library Feature Social Repo Content Repo Activity API Content API Multiple Similar Apps Single App Multiple Different Apps App Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API Auth API Database Login Feature User Repo App Account Feature Library Feature Social Repo Content Repo Activity API Content API