Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
俺コン Vol.1: Layered Architecture x RxSwiftを活用した適...
Search
Takeshi Ihara
October 02, 2017
Programming
3.4k
8
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
俺コン Vol.1: Layered Architecture x RxSwiftを活用した適切なエラーハンドリング
https://orecon.connpass.com/event/63769/
Takeshi Ihara
October 02, 2017
More Decks by Takeshi Ihara
See All by Takeshi Ihara
iOSDC20200921: Feature Flagを適切に分類することでA/Bテストの運用コストを下げる
nonchalant
3
1.4k
iOSDC 20190906: 動画アプリの投げ銭機能における 消耗型課金の仕組みと実装
nonchalant
3
6.4k
iOSDC 20190906: 動画アプリの投げ銭機能における 消耗型課金の仕組みと実装 with 発表ノート
nonchalant
2
630
Sign In with Apple
nonchalant
1
2.4k
iOSDC RejectCon 20180915: Factoryの自動生成によりテストを書きやすくする
nonchalant
1
750
iOSDC 20180902: 小さくはじめる端末管理
nonchalant
2
1k
devsap 20180728: コード生成のススメ
nonchalant
0
140
potatotips #50: iOSは自動生成の夢を見るか?
nonchalant
0
2k
try! Swift Tokyo 2018: Best Docker Container in Swift
nonchalant
1
1.4k
Other Decks in Programming
See All in Programming
act1-costs.pdf
sumedhbala
0
210
Creating Composable Callables in Contemporary C++
rollbear
0
200
The Bowling Game- From Imperative to Functional Programming - Part 1
philipschwarz
PRO
0
310
Welcome to the "Parametricity" 🏙️ − Generic だけど Specific な世界 −
guvalif
PRO
1
150
PHPだって関数型したい 〜できること、できないこと〜 / fp-in-php
jsoizo
0
190
その問い、本当に正しいですか?AI時代のエンジニアに必要な哲学と認知科学 / ai-philosophy-cognitive-science
minodriven
14
6.8k
エンジニアにデザインハーネスを 〜デザインプロセスを規定するためのハーネス〜 / Design harness from an engineer's perspective
rkaga
2
1.4k
継続モナドとリアクティブプログラミング
yukikurage
3
490
【やさしく解説 設計編 #1】「ドメイン駆動」と「実装駆動」ってなに? 〜設計の考え方を、たとえ話で学ぼう〜
panda728
PRO
1
110
20260623_Loop Engineeringで自分の分身の問い合わせBotを作る
ryugen04
0
200
技術記事、 専門家としてのプログラマ、 言語化
mizchi
14
7.4k
AIキャラアプリkaiwaの低遅延音声通話基盤をどう作ったか - AWS Gravitonで支える低遅延・低コストAI Agent基盤
mogamit
0
170
Featured
See All Featured
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
260
GraphQLとの向き合い方2022年版
quramy
50
15k
Automating Front-end Workflow
addyosmani
1370
210k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.7k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
A Modern Web Designer's Workflow
chriscoyier
698
190k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.2k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Done Done
chrislema
186
16k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Evolving SEO for Evolving Search Engines
ryanjones
0
240
Transcript
Layered Architecture x RxSwift Λ׆༻ͨ͠ దͳΤϥʔϋϯυϦϯά @nonchalant0303 Զίϯ Vol.1 /
Day. 1
ࣗݾհ • Takeshi Ihara • Recruit Marketing Partners • iOS
Engineer • Twitter: @nonchalant0303 • GitHub: Nonchalant
OSS https://github.com/Nonchalant/AppIcon
Kyobashi.swift
ΤϥʔϋϯυϦϯά
ΤϥʔϋϯυϦϯά ϓϩάϥϜͷॲཧதʹॲཧ͕͛ΒΕΔࣄ͕ ൃੜͨ͠ࡍɺͦͷॲཧΛΤϥʔͱͯ͠ରॲ͢Δ ॲཧͷ͜ͱͰ͋Δɻྫ֎ॲཧͱݺΕΔɻ
Α͋͘ΔΤϥʔ
None
Xcode
ͱΓ͋͑ͣ࠶ىಈ ͢Ε͍͍͔
ͳͥ࠶ىಈ͠Α͏ͱߟ͑ͨͷ͔ʁ աڈʹମݧͨ͜͠ͱ͕͋Δ ͦͷࡍʹ࠶ىಈͨ͠Β࣏ͬͨͨΊ
None
ϝʔϧΞυϨε͕ ؒҧ͍ͬͯΔʁ ύεϫʔυ͕ ؒҧ͍ͬͯΔʁ
ϝʔϧΞυϨε͕ ؒҧ͍ͬͯΔʁ ύεϫʔυ͕ ؒҧ͍ͬͯΔʁ αʔϏε յΕͯΔʂ αʔϏε յΕͯΔʂ
αʔϏεΕ
Ϣʔβʔ༷ʑ • ITϦςϥγʔ͕ߴ͍͔͍͔ • αʔϏεʹ׳Ε͠ΜͰ͍Δ͔Ͳ͏͔ • …
None
࣍ʹ͖͢ߦಈ͕ ͔Δ
దͳΤϥʔ
None
࣍ʹ͖͢ߦಈ͕ ͔Βͳ͍
ෆదͳΤϥʔ
None
ϝʔϧΞυϨε͕ ؒҧ͍ͬͯΔʁ ύεϫʔυ͕ ؒҧ͍ͬͯΔʁ αʔϏε յΕͯΔʂ αʔϏε յΕͯΔʂ
࣍ʹ͖͢ߦಈ͕ ͔Δ
దͳΤϥʔ
ΤϥʔϋϯυϦϯά ൃੜͨ͠ΤϥʔʹԠͯ͡ɺϢʔβʔʹݱࡏͷঢ় ଶΛࣔ͠ɺඞཁʹԠͯ࣍͡ͷߦಈΛࣔ͢͜ͱ
ΤϥʔϋϯυϦϯά͍͠
Τϥʔͷछྨ͕ଟ͍ • ωοτϫʔΫͷଓΤϥʔ • ೖྗ߲ͷؒҧ͍ʹΑΔΤϥʔ • λΠϜΞτʹΑΔΤϥʔ • …
1. ΤϥʔΛͻͨ͢Βચ͍ग़͢ ൈ͚࿙Εڐ༰͢Δ
2. ΤϥʔΛྨ͢Δ
2. ΤϥʔΛྨ͢Δ Πϯϑϥܥ ϏδωεϩδοΫܥ ωοτϫʔΫଓΤϥʔ ೖྗ߲ͷؒҧ͍ʹΑΔΤϥʔ λΠϜΞτʹΑΔΤϥʔ ϢʔβʔͷݖݶΤϥʔ ʜ ʜ
Layered Architecture *OGSBTUSVDUVSF 1SFTFOUBUJPO %PNBJO
Infrastructure Πϯϑϥܥ ωοτϫʔΫଓΤϥʔ λΠϜΞτʹΑΔΤϥʔ ʜ enum InfrastructureError: Error { case
connection case timeout … case unknown }
Domain ϏδωεϩδοΫܥ ೖྗ߲ͷؒҧ͍ʹΑΔΤϥʔ ϢʔβʔͷݖݶΤϥʔ ʜ enum DomainError: Error { case
input case permission ... case unknown }
Layered Architectureͷݪଇ ԼͷϨΠϠʔͷਓ্ͷϨΠϠʔ͕ެ։͍ͯ͠ ΔJOUFSGBDF͚ͩΛ͍ͬͯΔ
ݪଇʹͯ͠ࢀরͰ͖Δ *OGSBTUSVDUVSF 1SFTFOUBUJPO %PNBJO
Embedded Framework *OGSBTUSVDUVSF 1SFTFOUBUJPO %PNBJO
[Pros] Embedded Framework • Layerͷґଘ͕ؔ໌֬ʹͳΔ • ࠩϏϧυ͕ಇ͖͘͢ͳΔ • ෆඞཁͳϥΠϒϥϦ͕ࢀরͰ͖ͳ͘ͳΔ •
ModuleຖʹݴޠͷMigration͕Ͱ͖Δ
ϨΠϠʔؒͷܨ͗ࠐΈ *OGSBTUSVDUVSF 1SFTFOUBUJPO %PNBJO *OGSBTUSVDUVSF&SSPS %PNBJO&SSPS
InfrastructureErrorΛ PresentationʹൖͰ͖ͳ͍ InfrastructureErrorΛ DomainErrorʹม͢Δඞཁ͕͋Δ
InfrastructureError → DomainError *OGSBTUSVDUVSF&SSPS %PNBJO&SSPS DPOOFDUJPO DPOOFDUJPO UJNFPVU ʜ JOQVU
ʜ QFSNJTTJPO
ϩάΠϯ 1. ϝʔϧΞυϨεͱύεϫʔυೖྗ 2. ϩάΠϯ͢Δ 3. Success or Failure
• ྫ֎ΛUISPX͢Δ • force-tryΛ༻͍ͳ͍ͱࢀর͢Δϝιου ͯ͢ʹthrows͕ඞཁʹͳΔ 1. try-catch
*OGSB 1SFTFOUBUJPO %PNBJO ϩάΠϯ public protocol PublishRepository { func login(email:
String, password: String) throws -> User } public protocol LoginUseCase { func login(email: String, password: String) throws } public protocol LoginPresenter { func login(email: String, password: String) throws }
• ྫ֎Λ'BJMVSFઅʹ͢ • Switch-caseΛຖճॻ͘ඞཁ͕͋Δ • มॲཧΛ͠ͳ͍߹ͰDBTFจ͕ඞཁʹͳ Δ 2. Result
*OGSB 1SFTFOUBUJPO %PNBJO ϩάΠϯ public protocol PublishRepository { func login(email:
String, password: String) -> Result<User> } public protocol LoginUseCase { func login(email: String, password: String) -> Result<Void> } public protocol LoginPresenter { func login(email: String, password: String) -> Result<Void> }
• ྫ֎ΛPO&SSPSઅʹ͢ • catchErrorઅͰΤϥʔϋϯυϦϯά͢Δ • catchErrorઅ͕ͳ͍߹ͦͷ··ྫ֎͕ ԼྲྀʹྲྀΕΔ 3. RxSwift
*OGSB 1SFTFOUBUJPO %PNBJO ϩάΠϯ public protocol PublishRepository { func login(email:
String, password: String) -> Single<User> } public protocol LoginUseCase { func login(email: String, password: String) -> Completable } public protocol LoginPresenter { func login(email: String, password: String) -> Completable }
*OGSBTUSVDUVSF&SSPS %PNBJO&SSPS 1SFTFOUBUJPO&SSPS DPOOFDUJPO DPOOFDUJPO DPOOFDUJPO QFSNJTTJPO QFSNJTTJPO UFNQPSBSZ1BTTXPSE &YQJSFE
UFNQPSBSZ1BTTXPSE &YQJSFE BQJ3FQPOTF SFTQPOTF SFTQPOTF JOUFSOBMTFSWFSFSSPS VOLOPXO VOLOPXO USBOTMBUJPO
struct PublishRepositoryImpl: PublishRepository { func login(email: String, password: String) ->
Single<User> { return apiClient.create( with: PublishRequest(email: email, password: password) ) } } Infrastructure
struct LoginUseCaseImpl: LoginUseCase { func login(email: String, password: String) ->
Completable { return repository.login(email: email, password: password) .asObservable() .catchError { error -> Observable<User> in return Observable.error(DomainError.create(with: error)) } .flatMap { user -> Observable<Never> in guard user.hasPermission else { throw DomainError.permission } guard !user.isTemporaryPasswordExpired else { throw DomainError.temporaryPasswordExpired } return Observable.empty() } .asCompletable() } } Domain
struct LoginPresenterImpl: LoginPresenter { func login(email: String, password: String) ->
Completable { return useCase.login(email: email, password: password) .catchError { error -> Completable in return Completable.error(PresentationError.create(with: error)) } } } Presentation
֤ϨΠϠʔͰ ΤϥʔϋϯυϦϯάΛߦ͏
·ͱΊ • ΤϥʔͷೝࣝΛἧ͑Δ͜ͱʹΑΓυΩϡϝ ϯτԽͰ͖Δ • Embedded FrameworkʹΑΓΤϥʔʹ LayerͷࣝΛ༩Ͱ͖Δ • Layerͷܨ͗ࠐΈ֤ϓϩμΫτʹ߹Θͤͨ
ํ๏Λ࠾༻͠·͠ΐ͏