Slide 1

Slide 1 text

エラーアーキテクチャ設計について考える @iKichiemon #iOSDC Japan 2020

Slide 2

Slide 2 text

Terasaka Ikuya 株式会社セプテーニ・オリジナル ・GANMA!の開発 ・マネジメント業務 ・副業でFlutter/Firebase/Reactなど @iKichiemon #iOSDC Japan 2020

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

— モチベーション — iOSアプリでエラーハンドリングを改善した話 — Androidアプリでエラーハンドリングを改善した話 — エラーアーキテクチャ設計について考える Agenda 今日話すこと #iOSDC Japan 2020 ※ 資料で登場するコードは、骨子以外を省いて簡素化したものです。 そのまま利用できるとは限りません。

Slide 5

Slide 5 text

Agenda — モチベーション — iOSアプリでエラーハンドリングを改善した話 — Androidアプリでエラーハンドリングを改善した話 — エラーアーキテクチャ設計について考える #iOSDC Japan 2020

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

— モチベーション — iOSアプリでエラーハンドリングを改善した話 — Androidアプリでエラーハンドリングを改善した話 — エラーアーキテクチャ設計について考える Agenda #iOSDC Japan 2020

Slide 8

Slide 8 text

とあるiOSエンジニアは悩んでいた #iOS アプリ

Slide 9

Slide 9 text

とあるiOSエンジニアは悩んでいた #iOS アプリ ユースケースのエラーを もっと表現力豊かにしたいなぁ

Slide 10

Slide 10 text

とあるiOSエンジニアは悩んでいた #iOS アプリ 彼のプロジェクト(GANMA!)では、 RxSwiftを利用しています ユースケースのエラーを もっと表現力豊かにしたいなぁ

Slide 11

Slide 11 text

とあるiOSエンジニアは悩んでいた #iOS アプリ ユースケースのエラーを もっと表現力豊かにしたいなぁ 彼のプロジェクト(GANMA!)では、 RxSwiftを利用しています クリーンアーキテクチャを ベースにした設計を採用しています

Slide 12

Slide 12 text

とあるiOSエンジニアは悩んでいた #iOS アプリ 彼のプロジェクト(GANMA!)では、 RxSwiftを利用しています クリーンアーキテクチャを ベースにした設計を採用しています APIやRepositoryの返り値は、 Single を利用しています ユースケースのエラーを もっと表現力豊かにしたいなぁ

Slide 13

Slide 13 text

#iOS アプリ 設計概要

Slide 14

Slide 14 text

#iOS アプリ 設計概要

Slide 15

Slide 15 text

Singleの利用 #iOS アプリ

Slide 16

Slide 16 text

Singleのエラー処理 #iOS アプリ

Slide 17

Slide 17 text

Singleのエラー処理 エラーが起きた情報以外、謎やな どんなエラーを考慮せなあかんか、 get()の実装を見にいくしかないやん #iOS アプリ

Slide 18

Slide 18 text

Singleのエラー処理 #iOS アプリ switch ..case .. で、 エラーケースの処理を 書き忘れても気づかれへん ってのも危ないなぁ

Slide 19

Slide 19 text

改善したいポイントは、 ・どんなエラーが起こりえるかの情報がない ・Error型をそのまま利用すること やな とあるiOSエンジニアは整理した #iOS アプリ

Slide 20

Slide 20 text

・どんなエラーが起こりえるかの情報がない ・Error型をそのまま利用すること ↓ enumでエラーを定義して、 Either的な形にすれば、いい感じかもしれん とあるiOSエンジニアは理想を考えた #iOS アプリ

Slide 21

Slide 21 text

で処理する Single(Observable)をラップして 任意のError型を指定できるように #iOS アプリ

Slide 22

Slide 22 text

で処理する ※ここでのコードは かなり簡素化しています #iOS アプリ ↓のonErrorで渡ってきた型を変換する。 UseCaseError型の指定で振る舞いをつけることで UseCaseResultの中での変換を実現

Slide 23

Slide 23 text

UseCaseError型に制約をかけ E.applyの振る舞いを持たせる で処理する Error型を変換する、ということは、 default の考慮が必要 選択肢 ・その他を定義する case othres/unknown ・返り値をOptionalにする -> AppUseCaseError? ・落としてしまう default: fatalError() #iOS アプリ

Slide 24

Slide 24 text

とあるiOSエンジニアは取り込んでみた さっそく使こてみよ #iOS アプリ

Slide 25

Slide 25 text

利用してみる 包み込んであげたら終わり。 GANMA!では拡張メソッドを用意して、.toUseCaseResult() と書ける形に。 #iOS アプリ

Slide 26

Slide 26 text

利用してみる AppUseCaseErrorのデフォルトに、 othersを選択した場合は、ここで登場。 #iOS アプリ .othersも case の一つなので、 エラーケースが増えてもコンパイル時に気づくことができる。

Slide 27

Slide 27 text

onErrorでエラー型を指定するためにラッパークラス を定義した UseCaseResult エラーケースは enum で表現し、コンパイルで漏れ を検知できるようにした UseCase毎にエラーケースを定義し、個別エラーを表 現できるようにした 1. i. 2. 3. まとめ iOS アプリで したこと 2つの課題感 エラーケースを型で表現できない 考慮漏れをコンパイル時に検知できない 1. 2. 取り組んだこと

Slide 28

Slide 28 text

— モチベーション — iOSアプリでエラーハンドリングを改善した話 — Androidアプリでエラーハンドリングを改善した話 — エラーアーキテクチャ設計について考える Agenda #iOSDC Japan 2020

Slide 29

Slide 29 text

とあるAndroidエンジニアは悩んでいた #Android アプリ

Slide 30

Slide 30 text

とあるAndroidエンジニアは悩んでいた 機能の個別エラーを 追加しづらいです。 #Android アプリ

Slide 31

Slide 31 text

とあるAndroidエンジニアは悩んでいた 機能の個別エラーを 追加しづらいです。 彼のプロジェクト(GANMA!)では、 クリーンアーキテクチャを ベースにした設計を採用しています #Android アプリ

Slide 32

Slide 32 text

とあるAndroidエンジニアは悩んでいた 機能の個別エラーを 追加しづらいです。 彼のプロジェクト(GANMA!)では、 クリーンアーキテクチャを ベースにした設計を採用しています Either を導入しています #Android アプリ

Slide 33

Slide 33 text

とあるAndroidエンジニアは悩んでいた 機能の個別エラーを 追加しづらいです。 彼のプロジェクト(GANMA!)では、 クリーンアーキテクチャを ベースにした設計を採用しています Either を導入しています レイヤーをまたぐ時は、適宜、 それぞれのレイヤーのエラーに 変換して読み替えています #Android アプリ

Slide 34

Slide 34 text

#Android アプリ 既存のエラー処理に対する課題感 UseCaseErrorが、実質、下位レイヤーのエラーを マージしたものになっている状態 Serviceレイヤーのエラー Repositoryレイヤーのエラー

Slide 35

Slide 35 text

#Android アプリ 既存のエラー処理に対する課題感 汎用的な一般エラーが共通で処理されるのはいい。 ただ、特定の機能でしか発生しない固有エラーも、 全て共通のエラーに追加しなければいけない状態

Slide 36

Slide 36 text

#Android アプリ 既存のエラー処理に対する課題感 UseCaseエラーがどんどん太り、変換処理も、 全てのエラーを考慮しなければならず、無駄が多い。 個別のエラーはその機能だけが知っていれば十分なのに、 神エラーが全てを知っている状態となる。 固有エラーA 固有エラーB 固有エラーC 固有エラーD 固有エラーA 固有エラーB 固有エラーC 固有エラーD etc

Slide 37

Slide 37 text

とあるAndroidエンジニアは課題を整理した 個別のエラーも共通機構に 乗せざるを得ない状態 が問題だと考えられそうですね。 #Android アプリ

Slide 38

Slide 38 text

とあるAndroidエンジニアは解決策を考えた つまり、 ・機能ごとの固有エラーは個別定義 ・固有エラーは個別変換 ができればいい感じになりそうです #Android アプリ

Slide 39

Slide 39 text

#Android アプリ 解決方針 固有のエラーは、必要なユースケースでのみ処理するようにしたい

Slide 40

Slide 40 text

#Android アプリ 解決方針 ユースケース毎にエラー定義した場合は、共通のエラーケースが重複して発生する。 レイヤーをまたぐときに毎度変換するので、この重複部分は共通化しておきたい

Slide 41

Slide 41 text

とあるAndroidエンジニアは改善した やってみましょう #Android アプリ

Slide 42

Slide 42 text

#Android アプリ 機能固有のエラーを表すクラスを定義 レイヤーごとのエラーケースの中に、「FeatureSpecific」エラーを追加 “機能固有エラー"を表しており、内部に発生原因を持つ 固有エラーを個別定義

Slide 43

Slide 43 text

#Android アプリ 固有エラーを個別定義 共通のエラー定義に追加しなくても良くなる RepositoryLayerException.FeatureSpecific(AppSpecificError)

Slide 44

Slide 44 text

#Android アプリ 変換処理は共通+個別 共通の変換処理に固有エラーの変換を差し込めるようにする

Slide 45

Slide 45 text

変換処理は共通+個別 利用時のイメージ #Android アプリ 固有エラーの変換は、利用側が担うので、 共通機構はAppSpecificErrorを知らないで済む

Slide 46

Slide 46 text

Android アプリ で したこと FeatureSpecificというケースを追加 機能固有のエラーケースは別で定義して表現 共通変換処理に、個別変換を差し込めるようにする toUseCaseLayerReturns() -> Either 基本的なエラーケースは共通で処理する方針は維持 1. a. 2. i. 3. まとめ 課題感 個別のエラーを個別処理できない 1. 取り組んだこと #Android アプリ

Slide 47

Slide 47 text

— モチベーション — iOSアプリでエラーハンドリングを改善した話 — Androidアプリでエラーハンドリングを改善した話 — エラーアーキテクチャ設計について考える Agenda #iOSDC Japan 2020

Slide 48

Slide 48 text

ここまでの 点 # エラーについて える エラーケースを型で 表現できない Singleをラップした独自型を作成 エラー型を個別に指定できる形へ 1 エラーケースの漏れが発生 する可能性 2 エラーケースは列挙型で表現する 3 機能固有のエラーケースを 個別に処理できない 共通変換処理をベースにしつつ、 個別変換を差し込めるようにする

Slide 49

Slide 49 text

型で制約をかける 列挙型を利用する 固有エラーは個別に定義/処理する 1. 2. 3. 3つのポイント # エラーについて える

Slide 50

Slide 50 text

型で制約をかける 列挙型を利用する 固有エラーは個別に定義/処理する 1. 2. 3. の 点、ベースとなる思想 # エラーについて える 目指している姿 軸

Slide 51

Slide 51 text

# エラーについて える

Slide 52

Slide 52 text

# エラーについて える

Slide 53

Slide 53 text

型で制約をかける 列挙型を利用する 固有エラーは個別に処理する 1. 2. 3. 3つのポイント # エラーについて える どんなエラーが存在しているか 強調している ユースケースそれぞれの独立性を高めて、 その他の関心事から切り離している

Slide 54

Slide 54 text

# エラーについて える アーキテクチャにとって 強調・分離すべきユースケースとは

Slide 55

Slide 55 text

基本コース:晴れの日のシナリオ 代替コース:雨の日のシナリオ 全てが正しく動くときのステップ 基本的な利用方法を意味する (利用の90%) ユーザーが連載マンガを読む ユーザーがマンガをお気に入り登録する ユーザーが通常と異なる利用を行った 何か悪いことが起こった 基本コース以外のイレギュラーな利用方法 (利用の10%) ユーザーがログインをしていない 無料ユーザーが有料マンガを開く 2種類の ユースケース # エラーについて える ユースケース 動開発 ガイド

Slide 56

Slide 56 text

エラーアーキテクチャは、 雨の日のシナリオに焦点を当てる # エラーについて える アーキテクチャは、 晴れの日のシナリオと雨の日のシナリオの 2種類のユースケースに向き合っている

Slide 57

Slide 57 text

Single から UseCaseResult への 更を り る # エラーについて える Single UseCaseResult 晴れの日のシナリオは表現できているが、 雨の日のシナリオは実装依存になっている。 雨の日のシナリオを、AppUseCaseErrorで表現。 どんな雨の日を考慮すべきかが明確に定義されている。 onError: (error: Error) in onError: (error: AppUseCaseError) in

Slide 58

Slide 58 text

Single から UseCaseResult への 更を り る # エラーについて える Single UseCaseResult 晴れの日のシナリオは表現できているが、 雨の日のシナリオは実装依存になっている。 雨の日のシナリオを、AppUseCaseErrorで表現。 どんな雨の日を考慮すべきかが明確に定義されている。 onError: (error: Error) in onError: (error: AppUseCaseError) in 雨の日のシナリオをどうやって表現するか、を考えていくことが、 エラーアーキテクチャを設計する、ということ

Slide 59

Slide 59 text

雨の日のシナリオを 抽出していくには # エラーについて える

Slide 60

Slide 60 text

雨の日のシナリオに焦点を当てるには 「ほかに何か起きますか?」 の問いかけを繰り返し シナリオを抽出していく # エラーについて える

Slide 61

Slide 61 text

from「ユースケース駆動開発実践ガイド」

Slide 62

Slide 62 text

from「ユースケース駆動開発実践ガイド」

Slide 63

Slide 63 text

最後に

Slide 64

Slide 64 text

# エラーについて える

Slide 65

Slide 65 text

エラーアーキテクチャは、 ⾬の⽇シナリオを主 する は仕 みで る ⾬の⽇のシナリオは問いで抽 それぞれのユースケースは に ユースケースは、アプリにとっての存在意義 晴れの日、雨の日の両面で考えて考慮 列挙型は「どんな雨の日があるか」を主張するのに有用 I/Fでエラー型を明示することで、アピール 機能が増えるほど、ユースケースは増えていく それぞれの知識が混ざり合わないように注意したい あるユースケースの変更が、外に伝搬しないようにしたい ユースケース毎に雨の日を定義できる仕組みが望ましい より早い段階で実装に気付けるようにする コンパイルで気付けるのが望ましい 考慮もれに気付ける仕組み 方針があっても、それに沿えるかどうかは別問題 雨の日を表現するのに、必ずしも型で表現しなくても良い しかし、型で表現できた方が制約が強く表現力が高い 基本ケース以外に、他に何が起きるか?を問いかけ続ける エラーアーキテクチャ設計まとめ # エラーについて える

Slide 66

Slide 66 text

⽇のスライド GANMA!アプリ 使ってみてください〜!! #iOSDC Japan 2020 iKichiemon