FiNCのクライアントアーキテクチャを揃える試み / 20190110 #app_mp

704056da9a4c4e075ad14479beaebab7?s=47 takasek
January 10, 2019

FiNCのクライアントアーキテクチャを揃える試み / 20190110 #app_mp

【増枠】App Client Melting Pot #1「設計」 - connpass
https://app-client-mp.connpass.com/event/112973/
での発表資料です。

# 参考リンク

PEAKS(ピークス)|関 義隆, 松館 大輝, 史 翔新, 田中 賢治, 鈴木 大貴, 杉上 洋平 - iOSの設計パターンを入門から徹底解説「iOSアプリ設計パターン入門」執筆プロジェクト!
https://peaks.cc/iOS_architecture

マイクロサービスとクライアント: 
理想と現実の狭間で / Ideal and Reality of microservices from the client-side - Speaker Deck
https://speakerdeck.com/qsona/ideal-and-reality-of-microservices-from-the-client-side

10分で振り返るソフトウェアアーキテクチャの歴史2017 - Speaker Deck
https://speakerdeck.com/takasek/10fen-tezhen-rifan-rusohutoueaakitekutiyafalseli-shi-2017

アプリのアーキテクチャ ガイド  |  Android Developers
https://developer.android.com/jetpack/docs/guide#addendum

conference-app-2018/Result.kt at master · DroidKaigi/conference-app-2018
https://github.com/DroidKaigi/conference-app-2018/blob/master/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/Result.kt

Result V.S. Result - Speaker Deck
https://speakerdeck.com/inamiy/result-v-dot-s-result-t-e

マイクロサービスとクライアントとコンテキスト境界 / 20181030 #microserv - Speaker Deck
https://speakerdeck.com/takasek/20181030-number-microserv

実践、BFF ~ BFFはFiNCのアプリで何を解決したのか ~ - Speaker Deck
https://speakerdeck.com/kensukeizumi/shi-jian-bff-bffhafincfalseapuridehe-wojie-jue-sitafalseka

704056da9a4c4e075ad14479beaebab7?s=128

takasek

January 10, 2019
Tweet

Transcript

  1. FiNCͷ ΫϥΠΞϯτΞʔΩςΫνϟΛ ἧ͑ΔࢼΈ For App Client Melting Pot #1 2019/1/10

    FiNC Technologies Inc. / iOS Development Team ؔ ོٛ(takasek)
  2. !2 • ོؔٛ • !UBLBTFL • 'J/$5FDIOPMPHJFT • $MJFOU&OHJOFFS J045FBN

    ࣗݾ঺հ
  3. !3 • ʮJ04ΞϓϦઃܭύλʔϯೖ໳ʯ ڞஶ  • 1&",4ΑΓɺۙ೔Ұൠൃച։࢝ʂ IUUQTQFBLTDDJ04@BSDIJUFDUVSF .7$ .71

    .77. 'MVY 3FEVY $MFBO"SDIJUFDUVSF  ϞόΠϧಛ༗ͷىಈܦ࿏ɾը໘ભҠͷ੔ཧ·Ͱ໢ཏʂ ؔ͸ɺʮઃܭͱ͸Կ͔ʯͱ͍͏େ͖ͳओޠͰষ΄Ͳʂ ೥ .7$ ʙ͔Β࿈໖ͱଓ͘ ΞʔΩςΫνϟύλʔϯͷྺ࢙ͷ੔ཧʂ ϚαΧϦඞࢸͷϏοάΠγϡʔʹՌ׶ʹ௅Έ·ͨ͠ʂ એ఻
  4. FiNCͷ։ൃମ੍ɾαʔϏεߏ੒ 1

  5. !5 FiNCͷ։ൃମ੍ɾαʔϏεߏ੒ • J04 "OESPJEฒߦ։ൃ • Ϣʔβʔछผɺϓϥϯ͕ଟ͍ • Ұൠɺ՝ۚɺ๏ਓͳͲ •

    Ωϟϯϖʔϯ΍Πϕϯτ΋ൃੜ • ͝΄͏ͼ΢ΥʔΧʔͳͲ Ϗδωε୯ҐͰνʔϜΛ૊Ή
  6. !6 FiNCͷ։ൃମ੍ɾαʔϏεߏ੒ • ϥϯΩϯάαʔό͔ΒϥϯΩϯά৘ใΛऔಘ • ίϛϡχςΟαʔό͔ΒϥϯΩϯά։࠵άϧʔϓ৘ใΛऔಘ • ϥΠϑϩάαʔόʹ࠷৽ͷา਺ΛૹΔ Ұը໘಺Ͱଟ਺ͷଟ͘ͷαʔϏε͕࿈ܞ αʔόϨεϙϯεΛͦͷ··දࣔ͢Δ͚ͩͰ͸ͳ͍…ʂ


    ͨͱ͑͹า਺ϥϯΩϯάը໘Ͱ͸ɺ
  7. !7 qsonaʮϚΠΫϩαʔϏεͱΫϥΠΞϯτ: ཧ૝ͱݱ࣮ͷڱؒͰʯ p17 https://speakerdeck.com/qsona/ideal-and-reality-of-microservices-from-the-client-side?slide=17 ΫϥΠΞϯτʹϚΠΫϩαʔϏεͷϋϒ

  8. ΞϓϦΫϥΠΞϯτͷઃܭΛ
 ἧ͑Δඞཁ͕͋Δ

  9. !9 ຊ౰ʹἧ͑Δඞཁ͕͋Δ͔ʁ • ฒ΂ͯΞϓϦΛૢ࡞͢ΔౕͳΜ͍ͯͳ͍ • ׬શʹϢʔβʔମݧΛଗ͑Δඞཁ͸ͳ͍ • ͔͠͠ಉ͡ΞϓϦͳͷͰʮ࣮ݱ͍ͨ͜͠ͱʯ͸มΘΒͳ͍͜ͱ͕ଟ͍ • ϓϥϯɺΩϟϯϖʔϯɺ֎෦࿈ܞFUD

    • ϓϥοτϑΥʔϜʹΑͬͯཧ༝ͳ͘ڍಈ͕ҧ͏͜ͱ͸ආ͚͍ͨ →ϝϦοτ্͕ճΓͦ͏
  10. !10 ٕज़ཁ݅ͷݕ౼ • ڧ͍੩తܕ෇͚ݴޠʢ,PUMJO 4XJGUʣ • OVMM҆શ • ୅਺తσʔλܕʢTFBMFEDMBTT FOVNʣ

    • 3Y$PNNVOJUZ • 3FBDUJWF&YUFOTJPO →͍͚ͦ͏
  11. FiNCΫϥΠΞϯτʹదͨ͠ ΞʔΩςΫνϟͷݕ౼ 2

  12. Clean Architecture + MVVM

  13. MVVM (Model - View - ViewModel)

  14. !14 MVVM (Model-View-ViewModel) …ͷલʹɺ Presentation Domain Separation ʹ͍ͭͯઆ໌

  15. !15 Presentation Domain Separation • )VNBO*OUFSGBDF(VJEFMJOFTWT.BUFSJBM%FTJHO • QVTI NPEBMͰ؅ཧ͞ΕͨભҠߏ଄WTDPOUFYUͰ؅ཧ͞Εͨ"DUJWJUZ •

    6*5BCMF7JFXWT3FDZDMFS7JFX • FUD ϓϥοτϑΥʔϜʹΑͬͯઃܭࢥ૝ɾϑϨʔϜϫʔΫ͸େ͖͘ҟͳΔ ͔͠͠Model͸มΘΒͳ͍ ɹɹ˞ Model ͱ͸ʮσʔλߏ଄Λ͋ΒΘ͢ΦϒδΣΫτʯͰ͸ͳ͍ɻ ɹɹɹʮViewʹؔΘΒͳ͍஌ࣝʯ͢΂ͯͷ͜ͱɻ
  16. !16 MVVM (Model-View-ViewModel) ʮiOSΞϓϦઃܭύλʔϯೖ໳ʯୈ7ষΑΓ

  17. !17 MVVMͷݕ౼ • "OESPJE"SDIJUFDUVSF$PNQPOFOUTͰ͸-JWF%BUB5
 7JFXͷϥΠϑαΠΫϧʢը໘ճసͳͲʣʹ൥Θ͞ΕͣʹΠϯελϯεͷੜଘ؅ཧ͕Մೳʹ • J04Ͱ͸ඪ४ͷόΠϯσΟϯάϥΠϒϥϦ͸ͳ͍
 σϑΝΫτελϯμʔυ͸3Y4XJGU ΦϒβʔόಉظʢόΠϯσΟϯάϥΠϒϥϦΛ׆༻ʣ

  18. !18 MVVMͷݕ౼ • .PEFMͰ΋ඇಉظॲཧ͕ى͜Γ͏Δ͠ɺೖྗͱग़ྗͷؔ܎͸OOʹͳΔ • ίϚϯυΫΤϦ෼཭ݪଇʢ$24ʣ • ίϚϯυʢ෭࡞༻Λ൐͏ʣ͸౤͛Δ͚ͩ • ΫΤϦʢ෭࡞༻Λ൐Θͳ͍ʣ͸݁ՌΛड͚औΔ͚ͩ

    • .PEFM͔Βड͚औΔ݁Ռ΋0CTFSWBCMF ετϦʔϜ Ͱදݱ͍ͨ͠ AndroidͰ͸ɺLiveData͕͋Ε͹ผϥΠϒϥϦෆཁʁ NO! → ͲͪΒͷϓϥοτϑΥʔϜͰ΋ϦΞΫςΟϒϥΠϒϥϦ͕ཉ͍͠
  19. !19 MVVMͷݕ౼ MVVM with RxJava ❤ RxSwift RxͰἧ͑Ε͹ɺ྆ϓϥοτϑΥʔϜͷPR͕ޓ͍ʹಡΊΔͧ…ʂ

  20. MVVM

  21. Clean Architecture + MVVM

  22. Clean Architecture + MVVM ɹɹɹɹɹɹ↑ ɹɹɹɹɹɹɹʮ+ʯͱ͸ʁ

  23. !23 takasekʮ10෼ͰৼΓฦΔιϑτ΢ΣΞΞʔΩςΫνϟͷྺ࢙2017ʯ p5 https://speakerdeck.com/takasek/10fen-tezhen-rifan-rusohutoueaakitekutiyafalseli-shi-2017?slide=5 2छྨͷΞʔΩςΫνϟ

  24. !24 Clean Architectureͷݕ౼ MVVMʹΑͬͯPresentation Domain SeparationͰ͖ͨ…͜ΕͰສࣄղܾʁ NO! → Model಺ʹ΋ɺෳ਺ͷ৘ใΛద੾ʹऔΓ·ͱΊΔϨΠϠ͕ඞཁ •

    αʔϏεಛੑͱͯ͠ɺ
 .PEFM΋ෳࡶʹͳΓ͕ͪ
  25. !25 Clean Architecture ʮiOSΞϓϦઃܭύλʔϯೖ໳ʯୈ10ষΑΓ ֎→಺ͷ ґଘΛकΓ ςετɾ ࠶ར༻Մೳʹ

  26. !26 one more thing Resource<T, E> ܕ

  27. !27 Resource<T, E> ܕ • JO1SPHSFTT %PVCMF  • TVDDFTT

    5  • GBJMVSF & ඇಉظʹऔಘ͞ΕΔϦιʔεͷঢ়ଶΛɺ໢ཏੑΛ΋ͬͯදݱ͍ͨ͠ ໢ཏੑ͕͋Δ ʹ ύλʔϯϚονͰ͖Δ
  28. !28 Resource<T, E> ܕ ݩωλ(1): Android Jetpack
 https://developer.android.com/jetpack/docs/guide#addendum //a generic

    class that describes a data with a status public class Resource<T> { @NonNull public final Status status; @Nullable public final T data; @Nullable public final String message; private Resource(@NonNull Status status, @Nullable T data, @Nullable String message) { this.status = status; this.data = data; this.message = message; } public static <T> Resource<T> success(@NonNull T data) { return new Resource<>(SUCCESS, data, null); } public static <T> Resource<T> error(String msg, @Nullable T data) { return new Resource<>(ERROR, data, msg); } public static <T> Resource<T> loading(@Nullable T data) { return new Resource<>(LOADING, data, null); } }
  29. !29 Resource<T, E> ܕ ݩωλ(2): DroidKaigi/conference-app-2018
 https://github.com/DroidKaigi/conference-app-2018/blob/master/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/Result.kt sealed class Result<T>(val

    inProgress: Boolean) { class InProgress<T> : Result<T>(true) { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false return true } override fun hashCode(): Int = javaClass.hashCode() } data class Success<T>(var data: T) : Result<T>(false) data class Failure<T>(val errorMessage: String?, val e: Throwable) : Result<T>(false) companion object { fun <T> inProgress(): Result<T> = InProgress() fun <T> success(data: T): Result<T> = Success(data) fun <T> failure(errorMessage: String, e: Throwable): Result<T> = Failure(errorMessage, e) } } ύλʔϯϚον͠΍ͯ͘͢ྑ͍Ͷ
  30. !30 Resource<T> ͔ Resource<T, E> ͔ inamiyʮResult<T> V.S. Result<T, E>ʯ

    p7, 11 https://speakerdeck.com/inamiy/result-v-dot-s-result-t-e • ʮ͋Δ6TF$BTFͰى͜Γ͏ΔΤϥʔʯ
 ʮ͋Δ7JFX.PEFMͰى͜Γ͏ΔΤϥʔʯ͸ίϯςΩετ͕ҧ͏ • ໢ཏతʹදݱɾϋϯυϦϯά͍ͨ͜͠ͱ͕ଟ͍ • ʮΤϥʔʹͳΒͳ͍ʯ͜ͱ΋ࣔͤΔ
  31. !31 Resource.InProgress • ΞοϓϩʔυλεΫͳͲ΋ಉ͡3FTPVSDFܕͰදݱՄೳͰخ͍͠ • ਐ௙͕ඞཁͳ͍৔߹͸஋Λແࢹ͢Ε͹͍͍͚ͩͳͷͰࠔΒͳ͍ progress͸ਐḿ৘ใ(0.0ʙ1.0)Λ࣋ͭΑ͏ʹ

  32. !32 Resource × Rx Ͱ͞Βʹศརʹ extension Resource: ReactiveCompatible {} public

    enum ResourceTriggerSwitchingStrategy { case useFormer, useLatter } extension Reactive { public static func from<T, E: Error, Trigger: ObservableType>( trigger: Trigger, then execute: @escaping (Trigger.E) -> Single<T>, switchingStrategy: ResourceTriggerSwitchingStrategy ) -> Observable<Resource<T, E>> where Base == Resource<T, E> { let transform: (Trigger.E) -> Observable<Resource<T, E>> = { arg in return execute(arg) .asObservable() .map { .success($0) } .catchError { .just(.failure($0 as! E)) } .startWith(.inProgress(0)) } switch switchingStrategy { case .useFormer: return trigger.flatMapFirst(transform).share() case .useLatter: return trigger.flatMapLatest(transform).share() } } }
  33. ΞʔΩςΫνϟΛἧ͑ΔࢼΈ ͷ࣮ࡍͷͱ͜Ζ 3

  34. !34 Q. ຊ౰ʹiOS/AndroidΛἧ͑ΒΕͨͷʁ • 4XJGUͱ,PUMJO͸΍ͬͺΓҧ͏ • 4XJGUͰ͸δΣωϦοΫͳJOUFSGBDF͕࢖͑ͳ͍ • FOVN͸TFBMFEDMBTTͱҧ͍ɺ֤έʔεΛܕϨϕϧͰ۠ผ͠ͳ͍ •

    QBDLBHFͷཻ౓͕ҧ͍ʢ,PUMJO͸σΟϨΫτϦ୯Ґʣɺ໋໊ʹӨڹ • 3Y4XJGUͱ3Y+BWB͸ҙ֎ͱҧ͏ • *0εϨουपΓɺ1SPDFTTPS 'MPXBCMF CBDLQSFTTVSF • ΠϯελϯεͷϥΠϑαΠΫϧ • TVCTDSJCF EJTQPTFͷڍಈ • ࢀরΧ΢ϯτWTΨϕʔδίϨΫγϣϯ • ΦϖϨʔλͷ໊લ΋ࢥͬͨΑΓଗͬͯͳ͍ • ࠩ෼ߋ৽Λલఏʹ͢Δ͔Ͳ͏͔Ͱߟ͑ํ͕มΘΔ • "OESPJEʹ͸4VQQPSU-JCSBSZͰ௥Ճ͞Εͨ%JGG6UJM͕͋Δ • J04͸σϑΝΫτελϯμʔυ͕ͳ͍ A. େମ͍͚Δ͚Ͳɺ೉͍͠෦෼͸͋Δ
  35. !35 Q. ΞϓϦશମΛἧ͖͑ͬͨͷʁ • طଘίʔυ·ͰҰؾʹ߹ΘͤΔ͜ͱ͸Ͱ͖ͳ͍ • ίʔυϕʔε͕ҧ͏ͷͰɺϦϑΝΫλͳͲΛ͖͔͚ͬʹࠩҟ͸ग़Δ • ઃܭ͸઴ਐతʹਐԽͤ͞Δ΂͖ •

    Կ͕ʮ࠷৽ͷਖ਼ղʯ͔ݟ͑ʹ͍͘ͱ͍͏໰୊ʹ޲͖߹͏ • υΩϡϝϯςʔγϣϯͰରԠ͠Α͏ͱ͢Δͱ
 ϓϩμΫτίʔυ͔Βဃ཭͠΍͍͢  • ϛχϚϜͳઃܭύλʔϯΛఏࣔͰ͖ΔΑ͏ɺ
 αϯϓϧϓϩδΣΫτΛ੔͑Δ A. ͖ͬͱӬԕʹWIP
  36. !36 Q. ΞʔΩςΫνϟΛἧ͑ͯྑ͔ͬͨ఺͸ʁ • 6*ʹҾ͖ͣΒΕͣʹ
 ʮυϝΠϯ͸Կ͔ʯΛத৺ͱͨٞ͠࿦͕͠΍͍͢ • ࢓༷ͷߟྀ࿙Εɾઃܭͷѱ͞ʹؾ෇͚Δ • ࣮࣭ϖΞϓϩ

    • ࡉ͔͍ઃܭʹࠩҟ͕ग़͏Δ໰୊ʹରͯ͠͸ɺ
 ׬શΛٻΊΔ΂͖Ͱ͸ͳ͍͕ɺ
 ίϯςΩετΛ҆ఆͤ͞Δ౒ྗ͸େ੾ A. ୈҰʹ͸ɺυϝΠϯ΁ͷҙ͕ࣝڧ·ͬͨ͜ͱ
  37. !37 Q. ΞʔΩςΫνϟΛἧ͑ͯྑ͔ͬͨ఺͸ʁ takasekʮϚΠΫϩαʔϏεͱΫϥΠΞϯτͱίϯςΩετڥքʯ p6 https://speakerdeck.com/takasek/20181030-number-microserv?slide=6

  38. !38 Q. iOS/Androidͷಉ࣌։ൃͬͯඇޮ཰Ͱ͸ʁ • ࣗ૸Ͱ͖Δ෦෼͸ࣗ૸ͨ͠΄͏͕Α͍͜ͱ΋͋Δ • ϓϩτλΠϓϨϕϧͰ͸ยํ͕ઌߦ։ൃ • ยํ͕ઌߦϦϦʔεˠϑΟʔυόοΫΛݩʹ௥Ճ։ൃ A.

    ͦΕͳ
  39. !39 Q. Kotlin/NativeͬͯͲ͏ͳͷʁ • 4XJGUඇαϙʔτ • ,PUMJO0CK$4XJGUͷม׵͸Ͱ͖Δ͚Ͳɺ
 ಛʹTUSVDU΍୅਺తσʔλܕ͕࢖͑ͳ͍ͷ͸ݫ͍͠ • .PEFM૚ͷόΠφϦαΠζ͕ංେԽ͢Δʢ༧ײʣ

    • 7JFX૚Ͱ3Y4XJGU .PEFM૚Ͱ3Y+BWBΛೖΕΔΈ͍ͨͳʜ • .PEFM૚΋ͳΔ΂͘ϓϨʔϯʹߏ੒͢Δ͜ͱΛҙࣝ • ,PUMJOɺ4XJGUͰɺͲͪΒͰ΋BTZODBXBJU͕
 ࢖͑ΔੈքʹͳͬͨΒݟ͑Δ΋ͷ͕มΘ͖ͬͯͦ͏ A. কདྷʹظ଴
  40. !40 Q. ϩδοΫ͸αʔόαΠυʹدͤΔ΂͖Ͱ͸ʁ A. Ұ֓ʹͦ͏ͱ͸͍͑ͳ͍ takasekʮϚΠΫϩαʔϏεͱΫϥΠΞϯτͱίϯςΩετڥքʯ p23 https://speakerdeck.com/takasek/20181030-number-microserv?slide=23

  41. !41 Q. ϩδοΫ͸αʔόαΠυʹدͤΔ΂͖Ͱ͸ʁ A2. ͦͷ͏͑Ͱɺద੾ͳBFF(Backends for Frontends)͸༗ޮ • ৄ͘͠͸!J[NFBMͷൃදͰ
 IUUQTTQFBLFSEFDLDPNLFOTVLFJ[VNJTIJKJBOCGGCGGIBGJODGBMTFBQVSJEFIFXPKJFKVFTJUBGBMTFLB

    • %SPJE,BJHJ !RTPOB ΋ָ͓͠Έʹ
  42. !42 FiNC Technologies͸ࠓޙ΋ݚڀ͍͖ͯ͠·͢ʂ