Upgrade to Pro — share decks privately, control downloads, hide ads and more …

実践オブジェクト指向設計サブ資料:➁データモデル中心設計

Recruit
August 09, 2024

 実践オブジェクト指向設計サブ資料:➁データモデル中心設計

2024年度リクルート エンジニアコース新人研修の講義資料です

Recruit

August 09, 2024
Tweet

More Decks by Recruit

Other Decks in Technology

Transcript

  1. サブ資料②:データモデル中心の設計(データ中心アプローチ) データ中心アプローチとは データ中心アプローチは、アプリケーションやシステムの開発において、データを中心に設計・ 開発を行うアプローチで、伝統的なシステム開発の手法として使用されることが多い。このアプ ローチでは、まずシステムで扱うデータの定義や関連性を明確にし、それに基づいてアプリケー ションの機能や処理を構築していく Active Recordパターンを除いてデータモデルはビジネスロジックを持たず、アクセサ (getter/setter)のみが殆どで、その形式(データモデルの形式)をフレームワークから要求さ れることも少なくない

    今市場に出回っている主要なフレームワークの多くが、publicなプロパティを持つシンプルなオ ブジェクトを使うよう要求している。ほとんどの開発者は、貧血症のクラスの影響を受けざるを えない状況だ。私たちの置かれている状況はいわば慢性貧血症だ(IDDD本より) データの入れ物としてのオブジェクトを扱うトランザクションスクリプトの処理が複雑化・巨大 化しやすく、⾧期保守やしなやかな設計(変更に強い設計)が実現しづらくなる 小規模開発では問題にならないケースもあり、初期開発はスピーディに進むことが多いが、確実 に負債として蓄積されていく データモデルの特徴 ここでの「データモデル」は、DBのレコードに対応するオブジェクトを指し、基本はビジネス ロジックを持たず(ActiveRecordを除く)、アクセサ(getter/setter)が主な振る舞いになる 主要フレームワークの現状 データモデルの"振る舞い"について 1 / 12 ページ
  2. リクルートの開発現場とデータ中心アプローチ(トランザクションスクリプト) ルールや仕組みがシンプルな分、複雑化しやすいため、プロダクトの規模が大きくなると、 データ中心アプローチで作る場合は、フレームワークに則ってただ作るだけでは⾧期で複雑化し やすく、拡張性や保守性に問題が生じやすい。 そのため⾧期保守が前提となるリクルートのプロダクト開発においては、品質を保つための工夫 が必要になる(スピードも求められるため、様々な条件を考慮した選択をしないといけないケー スもある) トランザクションスクリプトとは ユースケース(アクション)を中心にオブジェクト(とその振る舞い)を組み立てる実装方法 で、データモデル中心設計とセットで語られることも多い。

    しかし、オブジェクト指向の原則に則っておらず、手続き型の持つ上位のモジュールが詳細なモ ジュールに依存することで変更に弱くなるという欠点を持ちやすかったり、ユースケース別に設 計されるため、複数のユースケースの間でロジックが重複して分散しやすい等の問題がある オブジェクトとデータベースの基本的な違い オブジェクトの参照性 オブジェクト指向プログラミングでは、オブジェクトは他のオブジェクトを直接参照することが できる。この参照により、複雑なデータ構造や関係を形成することが可能になる。 データベースの構造 リレーショナルデータベースでは、データはテーブルに行として格納され、各行は固定されたス キーマに従って列に分けられるため、各レコード間の関連付けは外部キーを通じて表現されるた め、オブジェクトの自然な関連付けとは異なる形で扱われる。 トランザクションスクリプトの原因 トランザクションスクリプトになりやすい理由 オブジェクト指向設計では、データとそのデータに関連する振る舞いをカプセル化することによ り、状態と振る舞いが一致するようにモデルを設計するが、リレーショナルデータベースを使用 する場合、以下の理由でトランザクションスクリプトに陥りやすくなる。 手続き型アプローチ オブジェクト指向のメリットを活かす代わりに、開発者がデータベースの操作に集中し、アプリ ケーションロジックが手続き型のスタイルで書かれることが多くなる。つまり、データベースの 制約により、ビジネスロジックが複数のステップにわたるトランザクションスクリプトとして表 現されることになりがち。 オブジェクト指向プログラミングとリレーショナルデータベース間のパラダイムの違いは、実際 に多くのソフトウェアプロジェクトでトランザクションスクリプトへの傾向を生じさせる重要な 要因で以下にその原因となる主な点を挙げる。 2 / 12 ページ
  3. Table Data Gateway (DAO) パターン データアクセスアーキテクチャ 商品を精算するための処理を行うControllerを作成する レジはCashierかCheck Out check-outは勘定という名詞なので、使える

    データモデルにおけるビジネスロジック さらにデータ中心アプローチでは、データモデルやデータベースの設計に重点が置かれるため、 ビジネスロジックの設計が犠牲になることも多い。これにより、設計の柔軟性や保守性が低下し やすい傾向がある。 特に大規模開発では、数百行もあるメソッドも少なくなく、処理が⾧くなりがちになる。デグレ のリスクも生じる データモデルを使用したトランザクションスクリプトを支えるデータアクセスのパターンは主要 なもので4種類ある ①Table Data Gatewayパターン テーブルにアクセスする機能を1つのクラス(もしくは単一のインスタンス)に、持たせる ※いわゆるDAOパターン 例:DAO、My Batis、JPA等 ②Row Data Gatewayパターン テーブルの行に相当するオブジェクトにデータ更新機能(新規/変更など)を持たせる ③Active Recordパターン テーブルにアクセスする機能およびビジネスロジックをクラス(もしくは単一インスタンス) に持たせる 例:Ruby on Rails ④Data Mapperパターン オブジェクトとデータベースデータとをマッピングする(いわゆるO/R)マッピング 例:Hibernate等 3 / 12 ページ
  4. Row Data Gatewayパターン Active Recordパターン データベースのテーブル、もしくはビューに対するラッピングクラス(インスタンス)であり、 データアクセスの実装やデータに対するビジネスロジックを隠蔽する Row Data Gatewayと非常に似ているが、Active

    Record自体がビジネスロジックを実装している点 が異なる。また、ビジネスロジックを実装している点でドメインモデルにも類似しているが、ド メインではなく、データとその関連性に焦点を当てたモデルであるため、その点で大きく異なる データベースのテーブル、もしくはビューに対するラッピングクラス(インスタンス)であり、 データアクセスの実装やデータに対するビジネスロジックを隠蔽する Row Data Gatewayと非常に似ているが、Active Record自体がビジネスロジックを実装している点 が異なる。また、ビジネスロジックを実装している点でドメインモデルとも類似しているが、ド メインに焦点を当てているのではなく、データとその関連性に焦点を当てたモデルであるため、 その点で大きく異なる 4 / 12 ページ
  5. Data Mapperパターン データモデルのクラスとテーブルのマッピング ORM(Object-Relational Mapping)等の仕組みを用いて、テーブルとオブジェクトをマッピングす るが、既存のデータベースを利用する場合、アプリケーションのデータモデルと既存DBのデータ モデル概念が一致しないこともあるため、テーブルを結合(JOIN)したデータモデルを用いるこ とも少なくない マッピングの注意点 テーブルからマッピングするのはDTOではなく、正式なデータモデルのエンティティであること

    (DTOは厳密にはモデルではなく、またオブジェクト指向設計の原則にも則っていない) DTOの適用範囲 DTO:publicなプロパティを持つシンプルなオブジェクト ORM等の仕組みを用いて、テーブルをマッピングすることが多いが、既存のデータベースを用い る際は、データモデルと既存DBの概念が一致しないこともあるため、テーブルをJoinしたデータ モデルを用いることも多い トランザクションスクリプトにおいて、DTOの主な使いどころは以下 ServiceからControllerへの受け渡し時 ControllerからViewへの受け渡し時 時々Repository内で使用(検索条件を保持するDTOや、Entityの一部の項目のみを定義したサマリ 用のDTO等)されることもある DTO(Data Transfer Object)はデータの「入れ物」として一定のメリットがあるものの、振る舞 い(ビジネスロジック)を持たないため、代わりにサービス層が複雑になりやすく、重複コー ドも生まれやすい等のデメリットも少なくない 5 / 12 ページ
  6. 補足:要求・要件の明確化 非機能要件は機能面以外の要件全般を指し、たとば「性能・信頼性・拡張性・運用性・セキュリ ティに関する要件」で近年非常に重要視されている。 たとえば以下 ①常に安定して動き続ける ➁速く動き続ける 機能要件と非機能要件 設計の前に要求・要件を明確にする必要がある 要件と要求 要件と要求は以下のようにビジネスレベル、業務レベル、システムレベルで分けられる

    ビジネス要求 「ビジネス要求」とは事業的に最も重要で戦略的に不可欠なもの 事業的に他と差別化されており、最優先で取り組まれる 業務要求 「業務要求」とは事業的に最も重要で戦略的に不可欠なもの 事業的に他と差別化されており、最優先で取り組まれる システム要求 「コアドメイン」とは事業的に最も重要で戦略的に不可欠なもの 事業的に他と差別化されており、最優先で取り組まれる ユーザ側からの要求を指す言葉。アジャイル開発では、これを元に開発チームと対話を重ねる 補足:ユーザストーリー 6 / 12 ページ
  7. R書店のデータモデルについて データモデルは基本、『実践データベース設計』で行ったデータモデルを元に作成されている前 提とする サービスについて データ中心アプローチではActive Recordパターンを除いて、ビジネスロジックはモデルが持つこ とが少なく、サービスが担うことなる 元々はモデルから派生しており、MVCアーキテクチャのうち、モデルのビジネスロジック部分を 切り離して収めたのが"サービス" "サービス"の起源

    データ中心設計のアプリケーションの基本的なアーキテクチャ データモデルを用いたアプリケーションは主に以下のような構造を持つ 引用元:https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRFzI0s- ihM14eHcwrcXSket8EZmNXlpQQVqaBE9daX5CKOwVLsP8zuSA9G6XpiGPzhy5Y&usqp=CAU データモデルを用いたMVCアプリケーションの構造 Presentation層 Application層 Data Source層 Controller Service Data Model DATA BASE Transaction Script Data Model 今回使用のDB H2 DB(Javaのアプリケーションを実行中のみ存在するDB)のPostgresSQL Modeで作成 アプリケーションが起動中のみDBが有効になる仕組み OBM(MyBatis) ObjectMapperとしてMyBatisを使用(JPAを使用した実装とは異なる) サービスに状態を持たせない 処理の一貫性を担保したり、テスタビリティを向上させる上で"サービス"に状態を持たせないこ とが重要 "サービス"実装時の注意点 7 / 12 ページ
  8. トランザクションスクリプトにおいて、サービス間の共通処理を集めたサービスをShared Service と呼ぶことがある 一部の設計パラダイムやフレームワークで用いられているServiceの概念で複数のControllerや個別 のServiceクラスで、共有・再利用 (※使い回される)されるロジックを提供する。 なお、SharedServiceクラスから個別のServiceクラスのメソッドを呼び出すことは一般には推奨さ れない(SharedServiceはあくまで呼び出される側) Shared Serviceについて

    R書店の各サービス(一例) なるべく単一責務のサービスとユースケースが1対1になるように気を付ける 店頭で表示する商品の取得・更新を担うサービス。商品検索にも対応する 注文情報の生成や、ステータスの更新等を担う 決済サービス 商品サービス 決済情報を生成、更新するサービス 決済ゲートウェイへのリクエストもこのサービスが担う(サービス内からゲートウェイクライア ントを呼び出して実行する) 注文サービス 顧客が商品を購入する際、決済、注文、配送などの処理を呼び出してまとめる役割を担う チェックアウト(レジ)サービス ・ユースケースとサービス(サービスは単一責務を担う)を基本1対1とする ・単一責務の原則(1つのクラスが複数の役割を持たせない。神クラス対策) ・コードの重複化や密結合を避ける方法を取ることが重要 この辺りは、後ほど紹介する副読本の『良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成⾧し続けるコードの書き方』にヒントとなる提案等が含まれている トランザクションスクリプトでコードの質が下がらないために必要なこと 商品の取得・更新を担うサービス。商品検索にも対応する カートサービス 8 / 12 ページ
  9. ポイント:インスタンスの生成ロジックの抽出してコードを整理すること DTOの生成等も含めてサービスに何でもかんでも書いてしまうと、サービスが肥大化し、可読性 も低くなっていきがちであるため、ビルダーパターンやファクトリパターン等を適用して隠ぺい することで、ソースコードの可読性や保守性を高めることが出来る GRASP原則の「生成者」に該当。生成する者を切り離すことで、生成する側と生成される側の依 存関係を緩和することができる 注意:N+1問題 毎回SQLを発行するN+1問題が生じがちであるが、時にパフォーマンス上の重大なボトルネックにな ることもあるので注意 引用元:https://miro.medium.com/v2/resize:fit:1400/0*E85cStg-NTVqi0Sq

    商品管理サービス 主にR書店の担当者が商品情報を登録したり管理する 商品在庫を管理するためのサービス(主に配送センター担当者向けのユースケースに基づく) 商品在庫管理サービス 主に顧客がレジで会計後、注文が生成されるタイミングで生成される、注文に紐づいた配送情報 を生成するサービス(※下段の配送管理サービスとは別) 配送サービス 配送情報を管理するためのサービス(主に配送センター担当者及び配送会社担当者向けのユース 配送管理サービス 9 / 12 ページ
  10. R書店のUtilityクラス/ Helperクラス static型の汎用処理群。使いすぎると手続き型に近くなるため、あまり、多用しない方が無難 技術的負債とリファクタリングについて R書店のコントローラ(Controller) Controllerにはビジネスロジックは持たせないのが基本(Thin Controllerが基本) リクルートの開発とリファクタリング リファクタリングとは、ソフトウェアの外部的振る舞いを保ちつつ、理解や修正が簡単になるよ うに、内部構造を改善することで、アジャイル開発のみならず、ウォーターフォール開発におい

    ても、ソフトウェアのコード品質を上げていく上では欠かせない取り組み。 常にエンジニアリングに対する事業価値を求められ(『事業価値とエンジニアリング』参照)、 リファクタリングに対しても同様の投資価値を求められる。 加えてテストコードを作らない(テストは手動で実施する)プロジェクトもあり、自動化したテ ストがないため、リファクタリングを実施しずらいプロジェクトもある 技術的"負債"の元々の捉え方 「技術的負債」の提唱者のウォード・カニンガムによると、本来の意味での技術的負債は金融の 借り入れと同様に(リファクタリング)で返済するもの。借入をすれば物事をより早く前に進め ることができる(つまり早くリリースできる)メリットもあるのでバランスが大事。 〝負債のメタファーで大事なのは返済してメタファーを味方につける力であり、それは問題を理 解するに従ってリファクタリングしていけるような、十分にきれいなコードを書いているかどう かで決まる〝 引用元:【翻訳】技術的負債という概念の生みの親 Ward Cunningham 自身による説明 - t-wadaの ブログ (hatenablog.jp) 但し「後できれいに書き直すつもりなら雑なコードを書いてもいい」という話ではないので注意 リファクタリングを怠ると、技術的負債が蓄積され、スパゲッティのようなプログラムになって いき、最終的には修正などを手を入れることができなくなる。このような状況に陥ると、プロ ジェクトが回らなくなり、失敗につながる。 但し、技術的負債をなるべく作らないことが絶対正しいとは限らない 技術的負債に対する考え方 10 / 12 ページ
  11. データ中心設計とドメインモデル中心設計(ドメイン駆動設計)の使い分け データ中心アプローチとドメイン駆動設計は、プロダクトの性質やプロジェクトの状況によって使い分 けることがあり、どちらのアプローチを選択するかは、以下のような要素に基づいて判断されることが ある。 プロダクトの複雑さ プロダクトが複雑でドメイン知識が重要な場合、ビジネスの複雑さに対応するためのモデルを作り上げ ることに焦点を当てているドメイン駆動設計が適しているといえる 変更の頻度 プロダクトの要件やビジネスルールが頻繁に変更される場合、ドメイン駆動設計は柔軟性と変更への対 応力を提供する。一方で変更が少なく安定したデータ操作が主体(特定のDBへの参照や書き込みがメイ

    ン)の場合、データ中心アプローチが効果的といえる プロジェクトの期間とリソース プロジェクトの時間やリソースが制約されている場合、データ中心アプローチは開発の迅速な進行を可 能にする。データベースアクセスに重点を置くことで、開発の効率性を高めることができる チームのスキルと経験 チームメンバーのオブジェクト指向やドメイン駆動設計に関するスキルや経験によっても選択が影響を 受けることがある。適切な訓練やサポートがなければ、ドメイン駆動設計の導入は困難であることが少 なくない。 その他、プロダクトの性格やプロジェクトの状況を考慮し、ドメイン駆動設計とデータ中心アプローチ を適切に組み合わせることもある。例えば、データ中心アプローチをベースにしながら、一部の重要な ドメインに対してはドメイン駆動設計を導入するというアプローチなどが挙げられる 大規模開発におけるデータモデル中心設計とドメインモデル中心設計(ドメイン駆動設計) データ(モデル)中心設計では、主にデータベースのテーブル構造や関連性に基づいて設計を 行い、実装においてはその設計を反映させることが主眼となり、アプリケーションの設計・実 装においてはシンプルなルールで行うことが可能であるため、メンバのバックグラウンドや経 験値が一定でないことが少なくない大規模開発で選択されることが多い 一方ドメイン駆動設計では、ビジネスドメイン自体に焦点を当てながら設計を行うため、デー タの関連性だけでなく、ビジネスルールやドメインオブジェクトの振る舞いなども設計に反映 されることになるが、要件とモデリング・実装のプロセスを繰り返し(アジャイル開発が前 提)が必要となる。そのため大規模開発で選択されることの多いウォーターフォール開発で は、フェーズごとに厳格な手順を踏むため、各フェーズでの要件や設計の変更は困難であり、 ドメイン駆動設計のような柔軟性を持ったアプローチとは相性があまり良くないと言える。し かしながら、例えば、ウォーターフォール開発の要件定義フェーズで十分なドメイン知識を収 集し、それを基にしたドメインモデルを作成する等、ウォーターフォール開発にドメイン駆動 設計の要素を取り入れることは可能といえる 12 / 12 ページ