Slide 1

Slide 1 text

レガシーなプロダクトからドメ イン層を再設計する 高橋 陽太郎 石井 潤

Slide 2

Slide 2 text

自己紹介 石井 潤 ● 株式会社リクルート( 2018.04-) ● iOS/Android App Developer (2018.10-) 高橋陽太郎 ● 株式会社リクルート ● Engineering Manager

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

これまでのあらすじ:混乱に秩序をもたらすリアーキテクチャ奮闘中 での出来事 View 肥大化。 ロジックやデータが モリモリに入ってい る Presenter ほぼログ送信のロ ジックしかなく薄すぎ る Model APIやDBアクセス レイヤリングを適切にする ことでFat Viewから脱却 したい。 なぜか依存している DBアクセス用のモデル を生成してたりする

Slide 5

Slide 5 text

リアーキテクチャは困難に困難を極め、 スコープを絞り、一旦既存制約を切り離したPoC実 装に方針を変換し、、、 時は流れ

Slide 6

Slide 6 text

そしてFat Presenterが出来上がった View 表示とイベント通知 (簡素化に成功) Presenter 画面表示のための データやフラグ、 ビジネスロジック。 大量の状態を持っ て肥大化。 UseCase ほぼRepositoryの ラッパー Repository ほぼDataAccessの ラッパー DataAccess APIやDBアクセ ス DomainModel

Slide 7

Slide 7 text

これがやりたかったことではなかったはずなのに、どうしてこうなったんだろう? View 表示とイベント通知 (簡素化に成功) Presenter 画面表示のための データやフラグ、 ビジネスロジック。 大量の状態を持っ て肥大化。 UseCase ほぼRepositoryの ラッパー Repository ほぼDataAccessの ラッパー DataAccess APIやDBアクセ ス DomainModel 【疑問】 状態を置くレイヤーが適切ではない のでは? Presenter 画面表示のための データやフラグ、 ビジネスロジック。 大量の状態を持っ て肥大化。

Slide 8

Slide 8 text

チームでは毎週技術顧問のtwadaさんに相談ペアプロする時間を持っていた。 そこで相談してみた。 「twadaさん、リアーキテクチャしたらPresenterが多数の状態を持ってしまい肥 大化してしまいました。これらの状態を管理するレイヤーを新たに作るべきか悩 んでいます」

Slide 9

Slide 9 text

twadaさん「どのレイヤーがどんな責務を持つかという議論の前に (多くのエンジニアが陥りがちな問 題)、ドメインがどうなっているかを知ることが重要だよ」 「多くのエンジニアが陥りがちな問題ですが、 どのレイヤーがどんな責務を持つべきか という議論の前に、ドメインがどうなっているかを知ることが重要 です」

Slide 10

Slide 10 text

ドメインがどうなっているかを知る?? これを機に自分たちのドメインモデルは何か?どんな状態やイベント/状態遷移を含むの か、などを理解するためにモデリングをしてみることに。

Slide 11

Slide 11 text

自分たちがモデリングしたものを再びtwadaさんにみてもらうと 既存の実装で扱われているデータのモデリングになってしまっていて、 UI/API/DBの実 装の影響を強く受けてしまっていますね。 本来、タウンワークアプリの本質って何でしょうね? 何の価値を提供しているんで しょう? 例えば、「求人情報」が「既読ステー タス」を持っているのって何かおかし くない?

Slide 12

Slide 12 text

実装詳細に魂をひかれていた!? 現状の実装に引っ張られていて、結果として、「レイヤリングによる交通整理」をしただけ に過ぎなかった。 そこからの脱出のコツは、APIやDBにまつわるもの、データの永続化や取得の都合は、 モデリングの際はメモリが無限にあってデータは全てメモリに乗っていると考えること。 UIの都合について(ページ数)、表示の制限についてもまずは考えなくて良い。

Slide 13

Slide 13 text

ここまでのアドバイスから学んだことポイント 自分たちがやっていたのは、よく知られているアーキテクチャーを真似たレイヤリングと、ドメインモデリング を「技術的な側面」だけ見てやっていた。でも本当に我々に必要だったのはテクニックではなく、 「アプリの中に埋もれているドメイン・概念を捉え直すこと 」だった。 2回アドバイスをもらったが、結局それはどちらも同じことで、要するに ↓だとこの時気がついた。 タウンワークアプリの本質って何でしょうね? 何の価値を提供しているんでしょう?

Slide 14

Slide 14 text

では、タウンワークアプリにとっての本質は何か?なんのためにあ るのか? タウンワークは、ユーザーに対して、求人情報を提供して応募してもらうことで、ユーザーにマッチングを提供するこ とが本質。 ユーザー 求人 重要なドメイ ンモデルの 発見 関係性 (閲覧する、応募する etc)

Slide 15

Slide 15 text

知っていたはずなのに自明すぎて気づいていなかった

Slide 16

Slide 16 text

「ユーザー」の発見と、それまでのモデルへの違和感。 例えば、求人情報と既読ステータスを直接結びつけるモデリングをしていた。 しかし、既読はユーザーの行動の結果。 ユーザーと求人情報は独立して存在する。 そもそもユーザーがモデルとして表れていなかった(当然すぎて埋まっていた)

Slide 17

Slide 17 text

必要だったものは、アプリの本質から考えた基本構造のモデリング だった ユーザーをま ずは書く それにまつわる エンティティを書く ユーザーとエンティティとの関 係を洗い出す

Slide 18

Slide 18 text

ここまでの気づき ● 我々エンジニアはすぐに実装やレイヤリングに目がいってしまうが、それではドメイ ンモデルには辿り着かない。 ● 実装から離れて、0ベースでドメインのことを考える。 ○ まずはアプリの本質、どんな価値をユーザーに提供するかを考える ○ その際、UI/DB/APIなど実装上の制約は考えない ○ 別のUIや実行基盤でも考えてみて同じように価値を提供できるか確認する ● これらを意識してモデリングをしてみたら、レイヤリングに注力していた最初期から はアプリの捉え方、認識の仕方がまるっきり変わっていた。 ↑スライドの途中ですが一番伝えたかったことです。

Slide 19

Slide 19 text

本日は 「アプリの本質に注目してドメインモデルをしたら アプリの捉え方がまるっきり変わっていた」 という感動が伝わればゴール。 なのですが、 やってみた中で色々なTIPsもあり、学びが多かったので、残り時間でそれを 紹介していきます。 (時間無くなったら途中で終わるのであとは読んでみてください。)

Slide 20

Slide 20 text

TIP1: ドメインモデルには情報ではなく事実を持たせる 「掲載終了期限」には2通りの表現がある 「yyyy/mm/dd」「あと〜日で終了」 事実を目的に沿って加工したものが情報 そのため、ドメインには事実を持っていた方が変更に強い https://speakerdeck.com/twada/test-driven-architecture-aws-dev-day-tokyo-2018?slide=90(大元は『SQLアンチパターン』)

Slide 21

Slide 21 text

事実それとも情報?を深く考える ユーザーが求人 に関心を示すこと 「n回閲覧したこ と」は事実?情 報? 例えば、「ユーザーの滞在時間」を事実としてモデルに保持 すると、、、 「n回同じ原稿をみたユーザーに表示を増やしたい」といった 案件で、nの回数が変更になってもドメインモデルのデータ の保持の仕方には影響せず、加工する処理を変えてあげ れば達成できる。

Slide 22

Slide 22 text

TIP2: ドメインモデルは現実よりも複雑で豊かなり タウンワークにおける検索時の駅選択のモデリング 【仕様①】 基本的にはユーザーが 選択している都道府県 の駅が選択できる 【仕様②】 同一路線上の他の都道 府県の駅が選択できる 【仕様③】JR中央本線 の「新宿駅」を選択する と、別路線の新宿駅も 選択状態になる。 【仕様④】 ユーザーには選択した 都道府県+選択した駅 名で表示する

Slide 23

Slide 23 text

愚直にリレーションを書くとn:nの集合体のようなカオスに

Slide 24

Slide 24 text

First attempt: まずは直感的に書いてみる →駅と路線/都道府県の関係性がわかりにくい。。。 駅・路線・都道府県 にそれぞれ関係性 があるはずなのに、 表現しにくい

Slide 25

Slide 25 text

Try:「駅」という概念を「路線上の駅」と「地図上の駅」に 物理的な「駅」ではなく、アプリ上の概 念としての「駅」。同じ名前でもアプリ 上で区別することにした。 クラスベースではなく、インスタンス(具 体例)で考えるのもポイントだった。

Slide 26

Slide 26 text

気づき:現実世界と一致しなくてOK。より複雑で豊かに。 路線上の駅と都道府県を直接結びつ ける →ユーザーが選択中の都道府県で選 べる駅のリストの取得が容易になった

Slide 27

Slide 27 text

TIP3: アプリの中のドメインモデリング ● サーバーでモデリングを頑張りそれを表示するだけなら、アプリのモデリングは必要な い? ○ 確かにそういうデータも存在する ○ 例えば求人詳細の給与や時間を立体的なモデルにしてもうまみがない (左図) ● でも、我々アプリの本質は「ユーザーと求人とのインタラクション」を実現すること。それ にまつわるドメインモデリングはアプリでも必要だった。 ○ 例えば閲覧履歴や検索条件など、ユーザーと求人の関係を促進するためのデータはモデルにする価 値がある(右図)

Slide 28

Slide 28 text

多分この辺で時間がなくなるので あとは読んでおいてくださいスタイル (まとめへ)

Slide 29

Slide 29 text

TIPS4: モデルの評価(モデルの馴染む感をどう判断する?) ● ドメインモデルは単体で価値があるものではなく、ユースケースで利用されることに 価値がある ○ プロトタイプの段階では想定されるユースケースでの運用に耐えられれば良い ○ ユースケースが増えたときに不十分さに気が付く

Slide 30

Slide 30 text

書いたものを評価するときの三要素 1. 【理解容易性】コンテキストを共有していない人(未来のチームメンバー)も理解できる か 2. 【変更容易性】影響範囲を小さくできているか 3. 【テスト容易性】テストの書きやすさ

Slide 31

Slide 31 text

理解容易性 ● IDが並んでいるより名前がついている方がわかりやすい ● 内部構造に依存した名前は避ける ● チームに日本人しかいなければ日本語の名前をつけるのも良い 数字だとわ かりにくい 内部構造に依 存した名前で わかりにくい

Slide 32

Slide 32 text

変更容易性 ● enumのケースやif文、ダウンキャストが増えていくのはよくない兆候(具象を直接 扱っている) ○ デザインパターンの出番 ● 利用者はインターフェースのみを扱う ● インターフェースは極力シンプルにして、具象クラスに多くを任せる ○ インターフェースに変更があると、具象クラス全てに影響が出てしまうため

Slide 33

Slide 33 text

テスト容易性 ● まずテストが書ける状態かでいろいろ気付ける ○ 実際にテストを書こうとして不足しているユースケースに気がついた ● その上で、テストを書こうとしてみるといろいろと気づきがある、例えば ○ 疎結合になっているか≒依存性の注入などがしやすいか ○ 内部状態に強く依存していないか、同じインプットに対して同じアウトプットを返すか ○ インスタンスを生成する時に長大なセットアップを必要としていないか≒サイズは適切か

Slide 34

Slide 34 text

実際に書いてみて検証する 利用者視点でコードを書いて、使いやすいか、要求を満たせているか確かめる。方法は 複数あり、どちらもメリット・デメリットがある。 また、方法によらず、得られたフィードバックを元に改善し続けることがポイント。サービ スが続く限りモデルも進化し続けていく。 1. 実際にViewを作って検証 2. テストコードによって検証

Slide 35

Slide 35 text

実際にViewを作って検証 愚直な方法 󰢃色々時間がかかる ● Viewも作る、Presenterも作る、AppDelegateも作る、Factoryも作る… 󰢏やることはわかりやすい なんたって本物だから ● 機能を漏らすことはない ● 実際作ってみたことでユースケースに足りない機能があることを発見できた

Slide 36

Slide 36 text

テストコードによる検証 利用者側の視点でIntegration Testを書く ● 使われるケースを想像しながら 󰢏検証のための最小限のコードで済ませられる 󰢃想像力がいる/ドメインモデリング未経験だと最初はハードルが高い

Slide 37

Slide 37 text

テストコードを書く際に気づいたミニTIP 1. テストはゴール(アサーション)から書くと目的がブレにくい a. 準備に手間がかかると目的を見失ったり、せっかく準備したのだからたくさ ん検証しようとなりがち 2. テストケースの名前の付け方、構造化の仕方、機能が先か、状況が先か a. 機能 -> 仕様書的にテストを書ける b. 状況 -> データの準備、使い回しがやりやすい

Slide 38

Slide 38 text

Viewを作る vs テストコード 我々の場合は、モデリングが不慣れだったことから、最初にViewを作って利用イメージ を作り、その後テストコードでの検証も取り入れて行った。(twadaさんに導いてもらって だんだん習熟度が上がって行ったのも大きかった) View テストコード 習熟度 👍低くても可能 ある程度必要 コード量 動かすためのコードが結構必要 👍必要最小限 パターン網羅 データ準備や操作が大変 👍データ準備は必要、あとは実行するだけ 繰り返し検証 大変 👍簡単

Slide 39

Slide 39 text

TIP5:モデルもメンテナンスする? モデルとコードの関係 ● モデルとコードは一体 ● 行ったり来たりしてフィードバックし合う ○ モデルだけだと抽象的すぎて要素を見落とすことがあるし、コードだけだと具体的すぎて全体が見えなく なる ○ どちらかで変更があればもう一方にも反映する ■ 項目→応募入力項目に名前変更 ● コードが書けたならモデル(図)はメンテナンスしなくて良い? ○ それぞれ目的が違う ■ モデルは全体を俯瞰するために有用 ● 全体像、要素の関係を把握しやすい ● 仕様変更(新たな仕様)の際に全体の中での位置、既存の要素との関係がすぐわかる モデル コード 全体、俯瞰、 要素の関係 具体、詳細

Slide 40

Slide 40 text

まとめ このセッションで触れた内容 ● アーキテクチャに秩序をもたらすべく、リアーキテクチャに取り組んだらFat Presenterになってしまった ● レイヤリングに意識を向けていたが、ドメインを掘り起こす作業の中で、アプリケー ションの本質を理解することが重要だと気づいた ● モデリングする上でたくさん疑問にぶつかり、それらに自分たちなりの解答を出せ た ○ ドメインには事実を持たせる?情報を持たせる? ○ ドメインは現実世界のものと対応するべき? ○ サーバーサイドではなく、アプリでもドメインに注意を払う必要ってあるの? ○ 作ってみたドメインがしっくりきているかってどうやったらわかるの? ■ 気づいたらテストコードの書き方も練度が上がっていた ○ 動作するコードが出来上がったら、モデルもメンテナンスしなければいけないの?

Slide 41

Slide 41 text

一番衝撃的だったのは、ドメインに意識を向ける中で我々のアプリ の捉え方がガラッと変わったこと このセッションで触れた内容 ● リアーキテクチャに取り組んだらFat Presenterになってしまった ● レイヤリングに意識を向けていたが、ドメインを掘り起こす作業の中で、アプリケー ションの本質を理解することが重要だと気づいた ● モデリングする上でたくさん疑問にぶつかり、それらに自分たちなりの解答を出せ た ○ ドメインには事実を持たせる?情報を持たせる? ○ ドメインは現実世界のものと対応するべき? ○ サーバーサイドではなく、アプリでもドメインに注意を払う必要ってあるの? ○ 作ってみたドメインがしっくりきているかってどうやったらわかるの? ■ 気づいたらテストコードの書き方も練度が上がっていた ○ 動作するコードが出来上がったら、モデルもメンテナンスしなければいけないの?

Slide 42

Slide 42 text

この感動が少しでも伝わったのであれば幸いです。 もし今後ドメインに意識を向けたときに 同じ感動を共有できたらぜひ教えてください このセッションについてのフィードバックもいただけると、このセッ ションのドメインモデルも豊かになります!

Slide 43

Slide 43 text

ご清聴ありがとうございました!