Chatworkのドメインをモデリングした / Modeling Chatwork domain

Chatworkのドメインをモデリングした / Modeling Chatwork domain

C952d9f90c7ec6cb1b5513c05d3db536?s=128

yoshiyoshifujii

February 16, 2020
Tweet

Transcript

  1. Object-Oriented Conference Chatwork株式会社 FUJII Yoshitaka Chatworkのドメインを モデリングした

  2. 自己紹介 - FUJII Yoshitaka - @yoshiyoshifujii - Chatwork株式会社 - Scala関西

    - 登壇 - ScalaMatsuri2019 - 実践 Clean Architecture - 趣味 - ⛰ ⛺ ‍♂ 庭DIY
  3. 2011年3月にリリースされ早9年… さまざまなレガシーと呼べる要因…

  4. https://fortee.jp/phpcon-2019/proposal/bfbfa1bc-d0e0-4814-a1 03-4f6eee82c2eb

  5. Chatworkドメインをモデリングしたよ

  6. アジェンダ 1. 目的 2. レガシーを掘り起す 3. 言葉を定義する 4. パッケージにまとめる 5.

    ユースケースで検証する 6. Roleオブジェクト
  7. 目的 - Chatworkの現状を把握し理想のモデルを描く - 達成要因 - 現状ドメインの概念モデルを描く - 理想ドメインの概念モデルを描く

  8. レガシーを掘り起こす - 掘り起こすために取ったアプローチ - RDRA 2.0でトップダウン - データ構造を分析してボトムアップ

  9. RDRA 2.0でトップダウン - RDRAは、要件定義の工程を通してシステム 化するものは何かを明らかにする - システムコンテキスト図でシステムに関係す るアクターと外部システムを洗い出す - システムコンテキスト図の目的でまとまった

    資料などはなかった - 関係しそうな部署を洗い出し有識者によるレ ビュー - ほぼすべての部署 - SPOFとなる個の存在 - 特定の個人しかできない業務の発覚
  10. RDRA 2.0でトップダウン - 早く失敗することを優先 (Fail First) - 1つの業務に着目して、以下の図を描いた - ビジネスコンテキスト図

    - ビジネスユースケース図 - 業務フロー図 - バリエーション・条件図 - ユースケース複合図 - 情報モデル図 - 状態モデル図 - 業務を理解しモデルに落とすのはとても大変 - そもそも業務を把握していない - すべての業務ってどれぐらいあるのだろう… - 関係する部署すべてにヒアリング
  11. RDRA 2.0でトップダウン - 縦に遂行する労力と、横に展開する労力を見積る - その結果得られる成果物が、今の時点で必要か? - RDRA 2.0の成果物がとても有効なのは分かった -

    目的を再確認 - 現状を把握し概念モデルを描く - このプロジェクトの先には必要 - 仕様バグのエビデンス - いったん、このアプローチは止める
  12. データ構造を分析 - 既に存在するシステムなので、現状の成果物 に着目する - 情報モデル図の延長線上に出来る成果物 - RDBMSのテーブル定義、YAMLなど - 正規化されていない…

    - 意味のあるまとまりを理解するために コードを読む… - チョモランマ・ディペンデンシー - とにかく… - そのままの構造をJavaのクラスに書き起こす - JIG (Java Instant-document Gazer) で出力 - https://github.com/dddjava/jig
  13. データ構造を分析 - そのままの構造は、人が理解するには難しすぎた - 言葉の意味が分からない… - 料金プラン種類、料金プランコード、利用機能プラン、決済プラン種別 - 関連がフラットに多い -

    誤字か違う意味の文字か区別がつかない
  14. 言葉を定義する - 既存の言葉の意味を調べる - 有識者へのヒアリング - ドキュメントを熟読 - 「つまりこうだよね」って言葉を定義する -

    省略された言葉を正しい言葉にしたうえで分解する - その言葉が必要とされるシーンに合わせて分解する - つまりこういうことだよねと核心に迫る言葉に置き換える - 単純に言い換える - 言葉の置き換えを、リファクタリングで実施する
  15. 言葉を定義する - 言葉の置き換えは、クラスの分解になる - Javaのコンパイラーを活用し関連を持続したまま分解 - JIGで逐次確認しながら実施 - 少し構造が見えてきた

  16. パッケージにまとめる - 集約を意識してパッケージにまとめる - いきなり細かい単位で区切らない - 影響範囲が閉じている粒度で切る - 相互に関連しているクラスは共通の概 念を見出す

  17. ユースケースで検証する - Javaで概念モデルを書き起こしてきた - ユースケースをJUnitのテストに書く - モデルがユースケースを満たせるか検証する - ユースケースに必要なふるまいが構造に追加される -

    さらにモデルが洗練されて意味のあるまとまり、新たな概念が発見される
  18. Roleオブジェクト - DCIアーキテクチャ - https://digitalsoul.hatenadiary.org/entry/20100131/1264925022 - オブジェクト指向の本質が人間のメンタルモデルを捉えることにあるとした 上で、問題と解決方法を提示している - 問題は、構造を捉えることに長けている反面、ふるまいを捉えることが苦手

    - 特定のふるまいをどのクラスにおくべきか - エンティティクラスが大量のメソッドで肥大化する - 今回は、 Factory Method について捉えた
  19. Roleオブジェクト - グループチャットにメッセージを生成する Factory Methodを実装する - グループチャットは、メッセージを生成す るうえで、集約由来の生成ロジックを保持 しており、そのロジックを通してしか、 メッセージの生成を許可しない

    - グループチャットのふるまいとして持たせ ることは、良さそうに思える
  20. Roleオブジェクト - グループチャットを通して生成する別の集 約のエンティティとして、ファイルやタス ク、ライブ… - グループチャットが持つFactoryとしての メソッドが増え、肥大化する

  21. Roleオブジェクト - Factory Methodは、JIGでレポートすると相互の 関連で出力される package groupChat import message.Message trait

    GroupChat { def createMessage(accountId: AccountId, body: String): Message = ??? } package message import groupChat.RoomId trait Message { val roomId: RoomId }
  22. Roleオブジェクト - メッセージパッケージにRoleオブジェクトを作る - Scalaの場合、型クラスとして作成すると扱いやすい package message import groupChat.GroupChat trait

    MessageRole[A] { def createMessage(self: A)(accountId: AccountId, body: String): Message } object MessageRole { implicit val groupChatMessageRole = new MessageRole[GroupChat] { override def createMessage(self: GroupChat)(accountId: AccountId, body: String): Message = ??? } implicit def toMessageRoleOps[A: MessageRole](self: A) = new { def createMessage(accountId: AccountId, body: String): Message = implicitly[MessageRole[A]].createMessage(self)(accountId, body) } }
  23. 働くをもっと楽しく、創造的に