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

サブ資料⑤その他設計や実装についてのポイント

Recruit
August 10, 2023

 サブ資料⑤その他設計や実装についてのポイント

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

Recruit

August 10, 2023
Tweet

More Decks by Recruit

Other Decks in Technology

Transcript

  1. 1 ページ サブ資料⑤:その他設計や実装についてのポイント 名前の重要性 個々のクラスや関数、プロパティが持つ名前はアプリケーションの構造を⽀える重要な要素 名前 名前 名前 名前 名前

    アプリケーション 名前 名前 名前 命名が適切でない場合どうなるか ・元々意図されていた概念を正しく表現できず、既存の処理や設計の体系的な理解や把握が 難しくなる ・ソースコードの品質および保守性や拡張性を損なうことにもつながる ※これらは⼤規模開発の現場の多くで起こっている課題 【結論】 設計意図と実装との整合性 を保つ上で、名前(命名)が重要 「加盟店」の命名について 「加盟店」をそのまま英語にしたり、Clientという名前を適⽤しようとすると⼩さな商店主を含 むニュアンスが感じられない。⼤きなチェーンの法⼈から⼩店まで幅広くカバーできる名前と してここではMerchantを選択 「⾦融機関」の命名について 「⾦融機関」の対象は銀⾏に限らず、信⽤⾦庫等も含まれるが、ここではまとめてBankとする。 (Bankとまとめても違和感はない) クラスと関数(メソッド)の命名について クラスは名詞、関数(メソッド)は動詞とするのが望ましい またプロジェクト全体で統⼀することが⼤事
  2. 2 ページ サービス(Service)名の付け⽅ 共通クラス等でない限り、具体的な⽬的が分かるクラス名をつける。例えば TransactionServiceのような名前からは具体的な⽬的が⾒えにくい。RemittanceServiceや PaymentService等具体的につける パッケージ構成 理解・把握しやい構成にする。今回はcommonとpayment、remittance、chargeそれぞれ、境界づ けられたコンテキストに沿ってパッケージを構成 fictpay-ddd-jersey

    |- common |- payment |- charge |- remittance ID設計 複数のインスタンスとDBが前提 ⼤規模システムになると複数台のサーバとDB分割を組み合わせるため、UUID(注:複数バージョン あり)を使⽤する等、異なるインスタンスやタイムゾーンで重複しないIDを作成する必要がある 履歴系のモデル名について 決済、チャージ、送⾦の履歴についてDBのレコードをベースにするため、〇〇履歴 (XXXXRecord)の名前の⽅がしっくりくるかもしれない HistoryとRecordの違いを意識する ネット⼝座振替サービスの命名 「ネット⼝座振替」の候補を挙げると ・ネット⼝振(OnlineDirectDebit / InternetDirectDebit)※Direct Debitは「⾃動引き落とし」 ・ネット⼝座振替(OnlineAccountTransfer / InternetAccountTransfer) QR決済の⾦融機関⼝座からのチャージに関して、「Deposit(デポジット)」も⽤いられること があるが、例えば、銀⾏⼝座から直接引き落とされる場合や、クレジットカードを使⽤して チャージする場合などは、⼝座振替は⼀般的に「Debit(デビット)」と呼ばれる。 そのため、今回はOnlineDirectDebitを採⽤
  3. 3 ページ 途中でキャストされると桁落ちするリスクがある。受け⼿の環境に依然する設計は避ける 必ず⽂字を含めたり、頭を0以外の数字にする 0から始まる数字のみ⽂字列は使⽤しない システムで管理するシステムIDはなるべく外部に表⽰させないようにすること システムIDを暗号化すると⻑くなったりするので、別で代替IDを⽤意して(URLパス等に 指定しても)⾒栄えのするID発番⽅法を採⽤することもある 代替IDの使⽤ 今回は以下のIDを⽤意(例:カスタマ)

    ・システムID:外部には出さない設計 ・代替ID:クライアント側に返す外出し⽤のID ・表⽰⽤ID:ID検索等で画⾯に表⽰されるID(ニックネームのような) 今回のIDについて ロック(悲観的ロック)について MyBatisでは、ロック付きでデータを取得するにはFOR UPDATE句を使⽤することがで きる <!-- UserMapper.xml --> <mapper namespace="com.example.UserMapper"> <select id="selectUserForUpdate" resultType="com.example.User"> SELECT * FROM users WHERE user_id = #{userId} FOR UPDATE </select> </mapper> 今回は決済系のシステムであるため、処理途中でデータ不整合が起こらないようにロックをする ・チャージ / MPM決済時:カスタマのレコードのロック ・送⾦時:送⾦者・送⾦先の両⽅のカスタマのレコードのロック DBレコードのロック
  4. 4 ページ DI(依存性注⼊)/ Singleton /静的(Static)呼び出し よく混同されやすいDI(依存性注⼊)、Singleton(シングルトン)、静的(Static)呼び出しの説 明と使い分けは以下 ロックの解放について PostgreSQL 11.19の場合、MyBatisでロック付きでデータを取得する場合のクローズ処理は、

    通常のクエリ実⾏と同様に⾏われます。MyBatisは、クエリの実⾏が完了した後に⾃動的に リソースを解放します。このため、クローズ処理については特別な処理は必要はない ただし、MyBatisを使⽤する際には、適切に SqlSession を開始し、クエリ実⾏後に適切にク ローズすることが重要。 try (SqlSession session = sqlSessionFactory.openSession()) { // ロック付きのデータ取得クエリを実⾏ YourEntity result = session.selectOne("yourNamespace.yourQuery", yourParameters); // 取得したデータの処理 } // クローズ処理はここで⾃動的に⾏われる SQLセッションとは アプリケーションからDBに接続する際に確⽴するセッション 終了時に適切にクローズされる必要がある DI(依存性注⼊) 静的(Static)呼び出し DI(Dependency Injection)は、依存関係を外部から注⼊することで、クラス間の疎結合性を促 進するソフトウェアの設計⽅法で、クラスが⾃⾝の依存関係を解決するのではなく、フレーム ワークなどの外部環境が管理する。 なお、Spring FrameworkのDIコンテナで扱うオブジェクトは、デフォルトでシングルトンとして 扱われる。つまり、同じクラスのインスタンスを複数回⽣成するのではなく、1つのインスタ ンスをDIコンテナが保持し、それを注⼊することで、オブジェクトの再利⽤性やメモリ効率の 向上を実現しているが、必ずしもインスタンスが単⼀であることは保証されておらず、新しい インスタンスが⽣成されることもあるので注意 オブジェクトの状態に依存しないヘルパー(Helper)やユーティリティ(Utility)等のオー バーライドの必要性のない汎⽤処理や定数等を提供する場合に⽤いられる Singleton(シングルトン) クラスのインスタンスが1つしか作成されないことを保証するために使⽤されるオブジェクト の提供⽅法で、⼀般的に、グローバルな状態や設定や共有リソースを管理するために使⽤され ます。適切な使い分けは、システム内で唯⼀のインスタンスが必要な場合に適している。従っ てシステム内で唯⼀のインスタンスが必要な場合は、DIではなくシングルトンを使⽤すべき ※参考シングルトンパターン
  5. 5 ページ FactoryMethodの使⽤ インスタンスの⽣成箇所(newしている箇所)は1箇所にまとめることが望ましい(ドメイン 駆動設計では少なくともモデル⽣成箇所はFactyoryに集約しておくことが望ましい)が、引数 が多くなる場合等は可読性や保守性の観点から考慮(全体の設計の整理や他の⽅法の検討 等)が必要になる場合もある 1クラス1⽬的(単⼀責務原則) 1つのクラスは単⼀の⽬的を持つようにする また、また1クラス内で扱う概念を3〜5等に絞るのも有効

    役割が不明瞭な神クラスに注意 プロジェクトの運⽤期間が⻑くなってくると、アプリケーションの様々な階層で呼ばれ、名前も曖 昧で、役割が多彩過ぎてはっきりしないモジュールやクラスが出てくる 神クラスを作らない対策 ・関数(メソッド)やプロパティを安易にpublicにしない ・1クラスにつき役割は1つにする(単⼀責務原則) ・役割に合った明確な名前をつける 名前が曖昧かつ 役割も多彩 ⾊んなレベルでグローバ ルに呼ばれる 通常はDIを選択し、オブジェクトが1つだけである必要がある場合はシングルトン、ヘル パー(Helper)とユーティリティ(Utility)等のオーバーライドの必要性のない汎⽤処理 等はStatic呼び出しを選択する使い分けを基準にすると分かり易い。 その他AOP等の選択肢もある 3つの使い分け⽅
  6. 6 ページ 分岐のネストを抑える⼯夫:分岐を浅くする 下記のように分岐を浅くするためにすぐにreturnしてなるべくネストが深くならないようにする フラグの使⽤について フラグはあまり多⽤しないこと(組み合わせ爆発等のリスクを減らすため、先に別の⽅ 法でまとまらないか考える)。基本は有効 or 無効の2値であるため、bool型を⽤いる。 String型やint型等、3種類以上の他の値が⼊る余地のあるデータ型は使⽤しないこと

    トレーサビリティ:例外は握り潰さない ⾦融系のシステムは特に、セキュリティやデータの整合性の維持が重要で、調査もでき るように、様々なデータを履歴として保存しておく必要がある。 今回はクライアントからの要求と対抗先へのリクエスト結果も含めて履歴を作成する形 を採⽤している 例外の握り潰しとは 「例外をcatchするけどその中では何もしない」ことを指して「握りつぶす」という void someMethod() { try { 例外が発⽣する処理 } catch(Exception e){ // 何もしない(握り潰し) } }
  7. 7 ページ 関数(メソッド)の戻り値 特別な事情や理由がない限りは、void関数(メソッド)はなるべく避けて、戻り値を返すよう にするのが望ましい。戻り値を返さないと処理の連続性が損なわれたり、可読性やメンテナン ス性が下がったりする。 戻り値を返すことで以下のメリットがある ・メソッドの意図が明確になる ・メソッドの再利⽤性(戻り値を別の処理で再利⽤することができる) ・テストが容易になる(結果を検証できる)

    ただし、全てのケースで戻り値を返す必要があるわけではなく、状況に応じて適切な設計を⾏ うことが重要 分岐のネストを抑える⼯夫:ExceptionHandlerと例外のthrow エラーパターン等を全て分岐で処理しようとすると、ネストが複雑になってしまいがちである が、別ファイルで例外をハンドリングする仕組みを定義して、分岐途中でthrowする選択肢も ある 不要なアクセサ(GetterとSetter)の削除 不要なアクセサ(GetterやSetter)を削除することは、ドメインモデルの設計においてドメイ ンモデルのカプセル化を強化し、不必要なデータの公開や変更の制御を⾏うことで、副作⽤ を減らすことにつながる public int getName() { return name; } 例外をthrowすると上記の定義箇所でキャッチしてレスポンスを返す ▪Getter
  8. 8 ページ public void setName(String name) { this.name = name;

    } 値の直書きやマジックナンバーについて 定数等セットして、なるべくマジックナンバー等は少なくすること コピペで収集がつかなくなる 定数化 直書きの状態 OAuth2.0 OAuth 2.0は、アプリケーションがユーザーの代わりにリソースへのアクセスを要求するための標 準的な認可フレームワークで、クライアントアプリケーションがユーザー情報を直接扱わずに、制 限されたリソース(例: ソーシャルメディアのAPIやクラウドストレージサービス)へのアクセス権 を取得する(アクセストークンを取得)ことを主な⽬的とする。 なお、トークンの形式には特に制約はない Bearerトークン OAuth2.0において、ある対象へのアクセス制御(利⽤可否など)を担う許可証であるアクセ ストークン。Bearerは「持参⼈」つまり「トークンを持ってきた存在」を意味し、Bearerトー クンは交通の切符のようにBearer(それを持ってきた存在)にアクセス権限を与え、トークン の権利の確認はしない。HTTPヘッダーのAuthorizationフィールドに "Bearer " + トークン⽂字列 の形式で送信される Bearerトークンを⽤いた例:Googleのサインイン等 ※今回はJWTトークンをBearerトークンとして使⽤ ▪Setter
  9. 9 ページ JWT(JSON Web Token) JWTは、データの安全な転送や認証に使⽤されるコンパクトで⾃⼰完結型のトークンで、JSON 形式で情報を表現し、署名と暗号化⽤いて作成する。クライアントがサーバーに対して認証 情報を送信する場合、JWTはトークンとして使⽤され、サーバーはJWTの署名を検証してトー クンの信頼性を確認する JWTは以下の3つのパートから構成される

    Header(ヘッダー): JWTのタイプや使⽤する署名アルゴリズム等のメタデータを含むパート(JSON形式)でBase64 でエンコードされる。 Payload(ペイロード): 本体データやクレーム(クレームセット)が含まれるパート(JSON形式)で、発⾏者 (issuer)、有効期限(expiration)、主体(subject)等の情報が含まれる。ペイロードも Base64エンコードされる。 Signature(署名): ヘッダーとペイロードを結合し、署名キーを使⽤して署名された値。署名はヘッダーとペイ ロードの内容が改ざんされていないことを検証するために使⽤される HTTPSで暗号化した通信においてトークンをさらに暗号化する必要性 HTTPSは通信経路を暗号化し、データの送受信を保護する。しかし、以下のようなセキュリティ 上の理由からトークンをさらに暗号化する トークンの保護: トークンは認証や認可に使⽤される重要な情報で、HTTPSによる通信の暗号化 は、通信経路を保護するだけであり、トークン⾃体の保護までは保証されず、トークンが不正 に取得された場合、攻撃者がトークンを使⽤して不正なアクセスを⾏う可能性がある。そのた め、トークンを暗号化することで、不正なアクセスや情報漏洩を防ぐための追加のセキュリ ティ層を提供する データの機密性: トークンは⼀般にクレデンシャルや個⼈情報などの機密データを含んでいる。 HTTPSは通信経路を暗号化するが、通信の中⾝まで暗号化するわけではない。トークンを暗号化 することで、トークンの中⾝が保護され、第三者がトークンを読み取ることを防ぐ 認証と認可 認証(Authentication) 認証(Authentication): ユーザーが⾃⾝を証明し、アイデンティティを確⽴するプロセスを指す。 ユーザーは⾃分の資格情報(ユーザー名とパスワード、トークン、⽣体認証など)を提供して、 システムにログインすることで認証が⾏われ、認証によってユーザーのアイデンティティが確認 され、その後のアクセス権の制御や特権の付与に使⽤される。 両者の違いは、簡単にいうと認証はユーザーが「誰か」を確認し、認可はユーザーが「何を」 するかを確認するプロセスを指す。
  10. 10 ページ チャージリクエストの考え⽅ クライアント側に出来るだけ、システムにとって重要な情報はなるべく保持させないのが⼤前 提。 ⾦融機関名や⽀店、⼝座番号はクライアントが先に前段の処理でFict PAYサーバから取得した後、 それらの情報とチャージ⾦額を含めてリクエストしてきている想定 また、⼝座については、現在は同じ⽀店で複数の⼝座を持つことは管理の⼿間や犯罪防⽌等の 理由で禁⽌されていることが多いが、以前は保持できていた。

    そのため、同⼀⽀店で複数⼝座を持つカスタマがいることを想定した設計にしておく Fict PAYアプリ (iOS/Android) Fict PAYサーバ チャージリクエスト情報 認可(Authorization) 認可(Authorization): ユーザーが特定のリソースや機能に対してアクセス権限を持つことを許可 するプロセスを指す。認可は、認証が成功した後に⾏われ、認証が⾏われたユーザーに対して、 リソースへのアクセス許可(権限)が与えられ、システム内のリソースに対する操作やデータへ のアクセスが制御される
  11. 11 ページ リクエストの試⾏回数(リトライ)について 今回はPOSTリクエストが、主な主体になるが、リトライについては設定しない。 GETリクエストは安全で冪等(べきとう)な操作であるため、複数回の試⾏が問題ない場合が多 いが、POSTリクエストは⾮冪等(ひべきとう)な操作であり、重複データの⽣成や副作⽤を引 き起こす可能性があるため、1回の試⾏が⼀般的に適している。ただし、具体的な要件やシナリ オに応じて試⾏回数を調整する必要がある チャージエラーのパターン ※カッコ()はステータスコード

    ・バリデーションエラー(⼊⼒項⽬に不備等)(400) ・(⾦融機関含む)認証エラー(401) ・カスタマのステータスが無効(退会・ペンディング・強制退会)(400)※422 ・カスタマが存在しない(400)※422 ・チャージ額がFictPay残⾼総額超過(400)※422 ・チャージ額が1回のチャージ下限値未満(400)※422 ・チャージ額が1回のチャージ上限値超過(400)※422 ・チャージ額+Fict PAY残⾼がFict PAY残⾼の上限値超過(400)※422 ・チャージ額を含めた1⽇分の累積チャージ額が上限額超過(400)※422 ・サーバ内エラー(500) チャージのエラーパターン 決済エラーのパターン ※カッコ()はステータスコード ・バリデーションエラー(⼊⼒項⽬に不備等)(400) ・(決済代⾏含む)認証エラー(401) ・カスタマのステータスが無効(退会・ペンディング・強制退会)(400)※422 ・カスタマが存在しない(400)※422 ・チャージ額がFictPay残⾼総額超過(400)※422 ・チャージ額が1回のチャージ下限値未満(400)※422 ・チャージ額が1回のチャージ上限値超過(400)※422 ・チャージ額+Fict PAY残⾼がFict PAY残⾼の上限値超過(400)※422 ・チャージ額を含めた1⽇分の累積チャージ額が上限額超過(400)※422 MPM決済のエラーパターン
  12. 12 ページ デザインパターンとの向き合い⽅ サンプルコードに引っ張られ過ぎず、パターンのエッセンスを理解すること 今回触れた主なデザインパターン ・Façadeパターン ・Factory Methodパターン ・Value Objectパターン

    ・Singletonパターン ・DAOパターン 紛らわしい⽤語 エンティティ 設計において『エンティティ』と名前がついているものに以下のものがある ・ドメインモデル(ドメイン駆動設計)におけるの『エンティティ』 ・データモデル(データ中⼼アプローチ)における『エンティティ(DBのテーブルのレ コードに近い)』 ・特定のフレームワーク(Spring等)で定められている『Entity(DBのテーブル・レコー ドに近い)』 ・クリーンアーキテクチャ(Clean Architecture)の『エンティティ』 (次ページへ続く) 送⾦エラーのパターン ※カッコ()はステータスコード ・バリデーションエラー(⼊⼒項⽬に不備等)(400) ・Fict PAYの認証エラー(401) ・送⾦者のステータスが無効(退会・ペンディング・強制退会)(400)※422 ・送⾦先が存在しないまたは無効(400)※422 ・送⾦者と送⾦先が同⼀(400)※422 ・送⾦額がFict PAYの1⽇の送⾦額を超過(400)※422 ・送⾦額が1回の送⾦下限値未満(400)※422 ・送⾦額が1回の送⾦上限値超過(400)※422 ・送⾦額+受取側のFict PAY残⾼がFict PAY残⾼の上限値超過(400)※422 ・送⾦額を含めた1⽇分の累積送⾦額が上限額超過(400)※422 送⾦エラーのパターン
  13. 13 ページ 参考:MVCに関連したその他のアーキテクチャ MVVM MVVMアーキテクチャ(Model-View-ViewModel Architecture)は、クライアントサイドの アプリケーションやフロントエンド開発において、ユーザーインターフェースのロジッ クとビジネスロジックを分離し、テストや保守性の向上を図るために使⽤されるアーキ テクチャで、特にデータバインディングやイベント駆動のフレームワークとの組み合わ せで効果的に利⽤される。

    MVVMは、以下の3つの主要なコンポーネントから構成される Model(モデル): データやビジネスロジックを表現するコンポーネントで、アプリケー ションのデータの状態や操作を管理し、データの永続化やバリデーションなどを担当す る View(ビュー): ユーザーに表⽰されるインターフェースの部分を担当するコンポーネン トで、ユーザーがデータやコンテンツを⾒るための表⽰を提供し、ユーザーの操作を受 け付ける ViewModel(ビューモデル): ビューとモデルの間を仲介するコンポーネントで、ビュー に表⽰されるデータや操作に関連するロジックを保持し、ビューとモデルの間でデータ のやり取りを担当する。また、ビューの状態を監視し、必要な場合にモデルへの変更を 反映する 値オブジェクト コレらは共通する点も少なくないが、厳密にはそれぞれ異なるパラダイムの別の概念で ある。また通信においても『エンティティ』の名前がついている概念がある 値オブジェクトも同じ名前で異なるものがある ・値オブジェクト(DDD / Value Objectパターン) ・Imuutableな⼊れ物としてのValueObject。特定のパラダイムにおいて、単にImmutableな オブジェクトのことをValueObjectと呼ぶことがある。 通信におけるエンティティ エンティティ(entity) リクエストやレスポンスのペイロード(付属物)として転送される情報でエンティティ ヘッダーフィールドとエンティティーボディからなる 引用元:https://kn.itmedia.co.jp/kn/articles/1906/26/news033.html
  14. 14 ページ MTV MTVアーキテクチャ(Model-Template-View Architecture)はDjango(Python) で⽤いられて いるアーキテクチャで、MTVはModel(モデル)、Template(テンプレート)、View (ビュー)を表す。なお、MTVではMVCにおけるViewがTemplateで、ControllerがViewに 該当する FLUX

    Flux(フラックス)はFacebookによって提案された、アプリケーションの状態管理をする ためのデザインパターンおよびアーキテクチャで、特にユーザーインターフェース (UI)とデータのフローを管理するために⽤いられ、主にReactアプリケーションの開発 に使⽤されている 引用元:https://www.atlassian.com/blog/software-teams/flux-architecture-step-by-step 引用元:https://www.educba.com/django-architecture/ ここにテキストを入力
  15. 15 ページ クリーンアーキテクチャ ヘキサゴナルアーキテクチャ ヘキサゴナルアーキテクチャ(Hexagonal Architecture)は、ポート&アダプタ(Ports & Adapters)アーキテクチャとも呼ばれ、システムをコアのドメイン(ビジネスロジック) とそれに関連する外部要素(データベース、ユーザーインターフェース、外部システム など)との間で分離し、それらの間の結合度を低く保つことを⽬的とする。

    システムを中⼼に六⾓形の形をしていると想定し、その中⼼にドメインモデル(ビジネ スロジック)を配置する。このドメインモデルは、外部要素に依存せずに単独でテスト 可能であり、ビジネスルールを表現する クリーンアーキテクチャ(Clean Architecture)は、ロバート・C・マーティンによって提 唱されたアーキテクチャでソフトウェアの各層を疎結合に保ちながら、ビジネスロジッ クを中⼼に配置し、テストや変更の容易さを重視する。 なお、中央に配置されているエンティティ(Entities)は、ドメインモデル全体(ドメイ ン駆動設計における集約やエンティティ、値オブジェクトを含む)を表現している 引用元:https://qiita.com/takasp/items/f05ef45a6fadd916ffa8 引用元:https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html