$30 off During Our Annual Pro Sale. View Details »

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

きちえもん
September 20, 2020

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

きちえもん

September 20, 2020
Tweet

More Decks by きちえもん

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

  3. View Slide

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

    資料で登場するコードは、骨子以外を省いて簡素化したものです。
    そのまま利用できるとは限りません。

    View Slide

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

    View Slide

  6. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  13. #iOS
    アプリ
    設計概要

    View Slide

  14. #iOS
    アプリ
    設計概要

    View Slide

  15. Singleの利用
    #iOS
    アプリ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. ・どんなエラーが起こりえるかの情報がない
    ・Error型をそのまま利用すること

    enumでエラーを定義して、
    Either的な形にすれば、いい感じかもしれん
    とあるiOSエンジニアは理想を考えた
    #iOS
    アプリ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  63. 最後に

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide