Pro Yearly is on sale from $80 to $50! »

エラーアーキテクチャ設計について考える/Thinking about error architecture design

エラーアーキテクチャ設計について考える/Thinking about error architecture design

iOSDC Japan 2020

A35557d139d8d53246763a01ec847a3f?s=128

きちえもん

September 20, 2020
Tweet

Transcript

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

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

  3. None
  4. — モチベーション — iOSアプリでエラーハンドリングを改善した話 — Androidアプリでエラーハンドリングを改善した話 — エラーアーキテクチャ設計について考える Agenda 今日話すこと

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

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

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

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

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

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

  12. とあるiOSエンジニアは悩んでいた #iOS アプリ 彼のプロジェクト(GANMA!)では、 RxSwiftを利用しています クリーンアーキテクチャを ベースにした設計を採用しています APIやRepositoryの返り値は、 Single<T> を利用しています

    ユースケースのエラーを もっと表現力豊かにしたいなぁ
  13. #iOS アプリ 設計概要

  14. #iOS アプリ 設計概要

  15. Single<T>の利用 #iOS アプリ

  16. Single<T>のエラー処理 #iOS アプリ

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

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

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

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

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

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

  23. UseCaseError型に制約をかけ E.applyの振る舞いを持たせる <T, UseCaseError> で処理する Error型を変換する、ということは、 default の考慮が必要 選択肢 ・その他を定義する

    case othres/unknown ・返り値をOptionalにする -> AppUseCaseError? ・落としてしまう default: fatalError() #iOS アプリ
  24. とあるiOSエンジニアは取り込んでみた さっそく使こてみよ #iOS アプリ

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

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

  27. onErrorでエラー型を指定するためにラッパークラス を定義した UseCaseResult<T, E: UseCaseError> エラーケースは enum で表現し、コンパイルで漏れ を検知できるようにした UseCase毎にエラーケースを定義し、個別エラーを表

    現できるようにした 1. i. 2. 3. まとめ iOS アプリで したこと 2つの課題感 エラーケースを型で表現できない 考慮漏れをコンパイル時に検知できない 1. 2. 取り組んだこと
  28. — モチベーション — iOSアプリでエラーハンドリングを改善した話 — Androidアプリでエラーハンドリングを改善した話 — エラーアーキテクチャ設計について考える Agenda #iOSDC

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

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

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

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

    アプリ
  33. とあるAndroidエンジニアは悩んでいた 機能の個別エラーを 追加しづらいです。 彼のプロジェクト(GANMA!)では、 クリーンアーキテクチャを ベースにした設計を採用しています Either<L, R> を導入しています レイヤーをまたぐ時は、適宜、

    それぞれのレイヤーのエラーに 変換して読み替えています #Android アプリ
  34. #Android アプリ 既存のエラー処理に対する課題感 UseCaseErrorが、実質、下位レイヤーのエラーを マージしたものになっている状態 Serviceレイヤーのエラー Repositoryレイヤーのエラー

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

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

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

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

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

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

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

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

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

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

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

  46. Android アプリ で したこと FeatureSpecificというケースを追加 機能固有のエラーケースは別で定義して表現 共通変換処理に、個別変換を差し込めるようにする toUseCaseLayerReturns() -> Either<UseCaseLayerException,

    T> 基本的なエラーケースは共通で処理する方針は維持 1. a. 2. i. 3. まとめ 課題感 個別のエラーを個別処理できない 1. 取り組んだこと #Android アプリ
  47. — モチベーション — iOSアプリでエラーハンドリングを改善した話 — Androidアプリでエラーハンドリングを改善した話 — エラーアーキテクチャ設計について考える Agenda #iOSDC

    Japan 2020
  48. ここまでの 点 # エラーについて える エラーケースを型で 表現できない Singleをラップした独自型を作成 エラー型を個別に指定できる形へ 1

    エラーケースの漏れが発生 する可能性 2 エラーケースは列挙型で表現する 3 機能固有のエラーケースを 個別に処理できない 共通変換処理をベースにしつつ、 個別変換を差し込めるようにする
  49. 型で制約をかける 列挙型を利用する 固有エラーは個別に定義/処理する 1. 2. 3. 3つのポイント # エラーについて える

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

    える 目指している姿 軸
  51. # エラーについて える

  52. # エラーについて える

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

    どんなエラーが存在しているか 強調している ユースケースそれぞれの独立性を高めて、 その他の関心事から切り離している
  54. # エラーについて える アーキテクチャにとって 強調・分離すべきユースケースとは

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

    (利用の10%) ユーザーがログインをしていない 無料ユーザーが有料マンガを開く 2種類の ユースケース # エラーについて える ユースケース 動開発 ガイド
  56. エラーアーキテクチャは、 雨の日のシナリオに焦点を当てる # エラーについて える アーキテクチャは、 晴れの日のシナリオと雨の日のシナリオの 2種類のユースケースに向き合っている

  57. Single<T> から UseCaseResult<T, Error> への 更を り る # エラーについて

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

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

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

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

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

  63. 最後に

  64. # エラーについて える

  65. エラーアーキテクチャは、 ⾬の⽇シナリオを主 する は仕 みで る ⾬の⽇のシナリオは問いで抽 それぞれのユースケースは に ユースケースは、アプリにとっての存在意義

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