2022/09/11_iOSDC Japan 2022での、高橋/石井の講演資料になります
レガシーなプロダクトからドメイン層を再設計する高橋 陽太郎石井 潤
View Slide
自己紹介石井 潤● 株式会社リクルート( 2018.04-)● iOS/Android App Developer (2018.10-)高橋陽太郎● 株式会社リクルート● Engineering Manager
これまでのあらすじ:混乱に秩序をもたらすリアーキテクチャ奮闘中での出来事View肥大化。ロジックやデータがモリモリに入っているPresenterほぼログ送信のロジックしかなく薄すぎるModelAPIやDBアクセスレイヤリングを適切にすることでFat Viewから脱却したい。なぜか依存しているDBアクセス用のモデルを生成してたりする
リアーキテクチャは困難に困難を極め、スコープを絞り、一旦既存制約を切り離したPoC実装に方針を変換し、、、時は流れ
そしてFat Presenterが出来上がったView表示とイベント通知(簡素化に成功)Presenter画面表示のためのデータやフラグ、ビジネスロジック。大量の状態を持って肥大化。UseCaseほぼRepositoryのラッパーRepositoryほぼDataAccessのラッパーDataAccessAPIやDBアクセスDomainModel
これがやりたかったことではなかったはずなのに、どうしてこうなったんだろう?View表示とイベント通知(簡素化に成功)Presenter画面表示のためのデータやフラグ、ビジネスロジック。大量の状態を持って肥大化。UseCaseほぼRepositoryのラッパーRepositoryほぼDataAccessのラッパーDataAccessAPIやDBアクセスDomainModel【疑問】状態を置くレイヤーが適切ではないのでは?Presenter画面表示のためのデータやフラグ、ビジネスロジック。大量の状態を持って肥大化。
チームでは毎週技術顧問のtwadaさんに相談ペアプロする時間を持っていた。そこで相談してみた。「twadaさん、リアーキテクチャしたらPresenterが多数の状態を持ってしまい肥大化してしまいました。これらの状態を管理するレイヤーを新たに作るべきか悩んでいます」
twadaさん「どのレイヤーがどんな責務を持つかという議論の前に (多くのエンジニアが陥りがちな問題)、ドメインがどうなっているかを知ることが重要だよ」「多くのエンジニアが陥りがちな問題ですが、 どのレイヤーがどんな責務を持つべきかという議論の前に、ドメインがどうなっているかを知ることが重要 です」
ドメインがどうなっているかを知る??これを機に自分たちのドメインモデルは何か?どんな状態やイベント/状態遷移を含むのか、などを理解するためにモデリングをしてみることに。
自分たちがモデリングしたものを再びtwadaさんにみてもらうと既存の実装で扱われているデータのモデリングになってしまっていて、 UI/API/DBの実装の影響を強く受けてしまっていますね。本来、タウンワークアプリの本質って何でしょうね? 何の価値を提供しているんでしょう?例えば、「求人情報」が「既読ステータス」を持っているのって何かおかしくない?
実装詳細に魂をひかれていた!?現状の実装に引っ張られていて、結果として、「レイヤリングによる交通整理」をしただけに過ぎなかった。そこからの脱出のコツは、APIやDBにまつわるもの、データの永続化や取得の都合は、モデリングの際はメモリが無限にあってデータは全てメモリに乗っていると考えること。UIの都合について(ページ数)、表示の制限についてもまずは考えなくて良い。
ここまでのアドバイスから学んだことポイント自分たちがやっていたのは、よく知られているアーキテクチャーを真似たレイヤリングと、ドメインモデリングを「技術的な側面」だけ見てやっていた。でも本当に我々に必要だったのはテクニックではなく、「アプリの中に埋もれているドメイン・概念を捉え直すこと 」だった。2回アドバイスをもらったが、結局それはどちらも同じことで、要するに ↓だとこの時気がついた。タウンワークアプリの本質って何でしょうね?何の価値を提供しているんでしょう?
では、タウンワークアプリにとっての本質は何か?なんのためにあるのか?タウンワークは、ユーザーに対して、求人情報を提供して応募してもらうことで、ユーザーにマッチングを提供することが本質。ユーザー 求人重要なドメインモデルの発見関係性(閲覧する、応募する etc)
知っていたはずなのに自明すぎて気づいていなかった
「ユーザー」の発見と、それまでのモデルへの違和感。例えば、求人情報と既読ステータスを直接結びつけるモデリングをしていた。しかし、既読はユーザーの行動の結果。ユーザーと求人情報は独立して存在する。そもそもユーザーがモデルとして表れていなかった(当然すぎて埋まっていた)
必要だったものは、アプリの本質から考えた基本構造のモデリングだったユーザーをまずは書く それにまつわるエンティティを書くユーザーとエンティティとの関係を洗い出す
ここまでの気づき● 我々エンジニアはすぐに実装やレイヤリングに目がいってしまうが、それではドメインモデルには辿り着かない。● 実装から離れて、0ベースでドメインのことを考える。○ まずはアプリの本質、どんな価値をユーザーに提供するかを考える○ その際、UI/DB/APIなど実装上の制約は考えない○ 別のUIや実行基盤でも考えてみて同じように価値を提供できるか確認する● これらを意識してモデリングをしてみたら、レイヤリングに注力していた最初期からはアプリの捉え方、認識の仕方がまるっきり変わっていた。↑スライドの途中ですが一番伝えたかったことです。
本日は「アプリの本質に注目してドメインモデルをしたらアプリの捉え方がまるっきり変わっていた」という感動が伝わればゴール。なのですが、やってみた中で色々なTIPsもあり、学びが多かったので、残り時間でそれを紹介していきます。(時間無くなったら途中で終わるのであとは読んでみてください。)
TIP1: ドメインモデルには情報ではなく事実を持たせる「掲載終了期限」には2通りの表現がある「yyyy/mm/dd」「あと〜日で終了」事実を目的に沿って加工したものが情報そのため、ドメインには事実を持っていた方が変更に強いhttps://speakerdeck.com/twada/test-driven-architecture-aws-dev-day-tokyo-2018?slide=90(大元は『SQLアンチパターン』)
事実それとも情報?を深く考えるユーザーが求人に関心を示すこと「n回閲覧したこと」は事実?情報?例えば、「ユーザーの滞在時間」を事実としてモデルに保持すると、、、「n回同じ原稿をみたユーザーに表示を増やしたい」といった案件で、nの回数が変更になってもドメインモデルのデータの保持の仕方には影響せず、加工する処理を変えてあげれば達成できる。
TIP2: ドメインモデルは現実よりも複雑で豊かなりタウンワークにおける検索時の駅選択のモデリング【仕様①】基本的にはユーザーが選択している都道府県の駅が選択できる【仕様②】同一路線上の他の都道府県の駅が選択できる【仕様③】JR中央本線の「新宿駅」を選択すると、別路線の新宿駅も選択状態になる。【仕様④】ユーザーには選択した都道府県+選択した駅名で表示する
愚直にリレーションを書くとn:nの集合体のようなカオスに
First attempt: まずは直感的に書いてみる→駅と路線/都道府県の関係性がわかりにくい。。。駅・路線・都道府県にそれぞれ関係性があるはずなのに、表現しにくい
Try:「駅」という概念を「路線上の駅」と「地図上の駅」に物理的な「駅」ではなく、アプリ上の概念としての「駅」。同じ名前でもアプリ上で区別することにした。クラスベースではなく、インスタンス(具体例)で考えるのもポイントだった。
気づき:現実世界と一致しなくてOK。より複雑で豊かに。路線上の駅と都道府県を直接結びつける→ユーザーが選択中の都道府県で選べる駅のリストの取得が容易になった
TIP3: アプリの中のドメインモデリング● サーバーでモデリングを頑張りそれを表示するだけなら、アプリのモデリングは必要ない?○ 確かにそういうデータも存在する○ 例えば求人詳細の給与や時間を立体的なモデルにしてもうまみがない (左図)● でも、我々アプリの本質は「ユーザーと求人とのインタラクション」を実現すること。それにまつわるドメインモデリングはアプリでも必要だった。○ 例えば閲覧履歴や検索条件など、ユーザーと求人の関係を促進するためのデータはモデルにする価値がある(右図)
多分この辺で時間がなくなるのであとは読んでおいてくださいスタイル(まとめへ)
TIPS4: モデルの評価(モデルの馴染む感をどう判断する?)● ドメインモデルは単体で価値があるものではなく、ユースケースで利用されることに価値がある○ プロトタイプの段階では想定されるユースケースでの運用に耐えられれば良い○ ユースケースが増えたときに不十分さに気が付く
書いたものを評価するときの三要素1. 【理解容易性】コンテキストを共有していない人(未来のチームメンバー)も理解できるか2. 【変更容易性】影響範囲を小さくできているか3. 【テスト容易性】テストの書きやすさ
理解容易性● IDが並んでいるより名前がついている方がわかりやすい● 内部構造に依存した名前は避ける● チームに日本人しかいなければ日本語の名前をつけるのも良い数字だとわかりにくい内部構造に依存した名前でわかりにくい
変更容易性● enumのケースやif文、ダウンキャストが増えていくのはよくない兆候(具象を直接扱っている)○ デザインパターンの出番● 利用者はインターフェースのみを扱う● インターフェースは極力シンプルにして、具象クラスに多くを任せる○ インターフェースに変更があると、具象クラス全てに影響が出てしまうため
テスト容易性● まずテストが書ける状態かでいろいろ気付ける○ 実際にテストを書こうとして不足しているユースケースに気がついた● その上で、テストを書こうとしてみるといろいろと気づきがある、例えば○ 疎結合になっているか≒依存性の注入などがしやすいか○ 内部状態に強く依存していないか、同じインプットに対して同じアウトプットを返すか○ インスタンスを生成する時に長大なセットアップを必要としていないか≒サイズは適切か
実際に書いてみて検証する利用者視点でコードを書いて、使いやすいか、要求を満たせているか確かめる。方法は複数あり、どちらもメリット・デメリットがある。また、方法によらず、得られたフィードバックを元に改善し続けることがポイント。サービスが続く限りモデルも進化し続けていく。1. 実際にViewを作って検証2. テストコードによって検証
実際にViewを作って検証愚直な方法色々時間がかかる● Viewも作る、Presenterも作る、AppDelegateも作る、Factoryも作る…やることはわかりやすい なんたって本物だから● 機能を漏らすことはない● 実際作ってみたことでユースケースに足りない機能があることを発見できた
テストコードによる検証利用者側の視点でIntegration Testを書く● 使われるケースを想像しながら検証のための最小限のコードで済ませられる想像力がいる/ドメインモデリング未経験だと最初はハードルが高い
テストコードを書く際に気づいたミニTIP1. テストはゴール(アサーション)から書くと目的がブレにくいa. 準備に手間がかかると目的を見失ったり、せっかく準備したのだからたくさん検証しようとなりがち2. テストケースの名前の付け方、構造化の仕方、機能が先か、状況が先かa. 機能 -> 仕様書的にテストを書けるb. 状況 -> データの準備、使い回しがやりやすい
Viewを作る vs テストコード我々の場合は、モデリングが不慣れだったことから、最初にViewを作って利用イメージを作り、その後テストコードでの検証も取り入れて行った。(twadaさんに導いてもらってだんだん習熟度が上がって行ったのも大きかった)View テストコード習熟度 👍低くても可能 ある程度必要コード量 動かすためのコードが結構必要 👍必要最小限パターン網羅 データ準備や操作が大変 👍データ準備は必要、あとは実行するだけ繰り返し検証 大変 👍簡単
TIP5:モデルもメンテナンスする? モデルとコードの関係● モデルとコードは一体● 行ったり来たりしてフィードバックし合う○ モデルだけだと抽象的すぎて要素を見落とすことがあるし、コードだけだと具体的すぎて全体が見えなくなる○ どちらかで変更があればもう一方にも反映する■ 項目→応募入力項目に名前変更● コードが書けたならモデル(図)はメンテナンスしなくて良い?○ それぞれ目的が違う■ モデルは全体を俯瞰するために有用● 全体像、要素の関係を把握しやすい● 仕様変更(新たな仕様)の際に全体の中での位置、既存の要素との関係がすぐわかるモデル コード全体、俯瞰、要素の関係 具体、詳細
まとめこのセッションで触れた内容● アーキテクチャに秩序をもたらすべく、リアーキテクチャに取り組んだらFatPresenterになってしまった● レイヤリングに意識を向けていたが、ドメインを掘り起こす作業の中で、アプリケーションの本質を理解することが重要だと気づいた● モデリングする上でたくさん疑問にぶつかり、それらに自分たちなりの解答を出せた○ ドメインには事実を持たせる?情報を持たせる?○ ドメインは現実世界のものと対応するべき?○ サーバーサイドではなく、アプリでもドメインに注意を払う必要ってあるの?○ 作ってみたドメインがしっくりきているかってどうやったらわかるの?■ 気づいたらテストコードの書き方も練度が上がっていた○ 動作するコードが出来上がったら、モデルもメンテナンスしなければいけないの?
一番衝撃的だったのは、ドメインに意識を向ける中で我々のアプリの捉え方がガラッと変わったことこのセッションで触れた内容● リアーキテクチャに取り組んだらFat Presenterになってしまった● レイヤリングに意識を向けていたが、ドメインを掘り起こす作業の中で、アプリケーションの本質を理解することが重要だと気づいた● モデリングする上でたくさん疑問にぶつかり、それらに自分たちなりの解答を出せた○ ドメインには事実を持たせる?情報を持たせる?○ ドメインは現実世界のものと対応するべき?○ サーバーサイドではなく、アプリでもドメインに注意を払う必要ってあるの?○ 作ってみたドメインがしっくりきているかってどうやったらわかるの?■ 気づいたらテストコードの書き方も練度が上がっていた○ 動作するコードが出来上がったら、モデルもメンテナンスしなければいけないの?
この感動が少しでも伝わったのであれば幸いです。もし今後ドメインに意識を向けたときに同じ感動を共有できたらぜひ教えてくださいこのセッションについてのフィードバックもいただけると、このセッションのドメインモデルも豊かになります!
ご清聴ありがとうございました!