Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

自己紹介 - FUJII Yoshitaka - @yoshiyoshifujii - Chatwork株式会社 - Scala関西 - 登壇 - ScalaMatsuri2019 - 実践 Clean Architecture - 趣味 - ⛰ ⛺ ‍♂ 庭DIY

Slide 3

Slide 3 text

2011年3月にリリースされ早9年… さまざまなレガシーと呼べる要因…

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

アジェンダ 1. 目的 2. レガシーを掘り起す 3. 言葉を定義する 4. パッケージにまとめる 5. ユースケースで検証する 6. Roleオブジェクト

Slide 7

Slide 7 text

目的 - Chatworkの現状を把握し理想のモデルを描く - 達成要因 - 現状ドメインの概念モデルを描く - 理想ドメインの概念モデルを描く

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

RDRA 2.0でトップダウン - RDRAは、要件定義の工程を通してシステム 化するものは何かを明らかにする - システムコンテキスト図でシステムに関係す るアクターと外部システムを洗い出す - システムコンテキスト図の目的でまとまった 資料などはなかった - 関係しそうな部署を洗い出し有識者によるレ ビュー - ほぼすべての部署 - SPOFとなる個の存在 - 特定の個人しかできない業務の発覚

Slide 10

Slide 10 text

RDRA 2.0でトップダウン - 早く失敗することを優先 (Fail First) - 1つの業務に着目して、以下の図を描いた - ビジネスコンテキスト図 - ビジネスユースケース図 - 業務フロー図 - バリエーション・条件図 - ユースケース複合図 - 情報モデル図 - 状態モデル図 - 業務を理解しモデルに落とすのはとても大変 - そもそも業務を把握していない - すべての業務ってどれぐらいあるのだろう… - 関係する部署すべてにヒアリング

Slide 11

Slide 11 text

RDRA 2.0でトップダウン - 縦に遂行する労力と、横に展開する労力を見積る - その結果得られる成果物が、今の時点で必要か? - RDRA 2.0の成果物がとても有効なのは分かった - 目的を再確認 - 現状を把握し概念モデルを描く - このプロジェクトの先には必要 - 仕様バグのエビデンス - いったん、このアプローチは止める

Slide 12

Slide 12 text

データ構造を分析 - 既に存在するシステムなので、現状の成果物 に着目する - 情報モデル図の延長線上に出来る成果物 - RDBMSのテーブル定義、YAMLなど - 正規化されていない… - 意味のあるまとまりを理解するために コードを読む… - チョモランマ・ディペンデンシー - とにかく… - そのままの構造をJavaのクラスに書き起こす - JIG (Java Instant-document Gazer) で出力 - https://github.com/dddjava/jig

Slide 13

Slide 13 text

データ構造を分析 - そのままの構造は、人が理解するには難しすぎた - 言葉の意味が分からない… - 料金プラン種類、料金プランコード、利用機能プラン、決済プラン種別 - 関連がフラットに多い - 誤字か違う意味の文字か区別がつかない

Slide 14

Slide 14 text

言葉を定義する - 既存の言葉の意味を調べる - 有識者へのヒアリング - ドキュメントを熟読 - 「つまりこうだよね」って言葉を定義する - 省略された言葉を正しい言葉にしたうえで分解する - その言葉が必要とされるシーンに合わせて分解する - つまりこういうことだよねと核心に迫る言葉に置き換える - 単純に言い換える - 言葉の置き換えを、リファクタリングで実施する

Slide 15

Slide 15 text

言葉を定義する - 言葉の置き換えは、クラスの分解になる - Javaのコンパイラーを活用し関連を持続したまま分解 - JIGで逐次確認しながら実施 - 少し構造が見えてきた

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

ユースケースで検証する - Javaで概念モデルを書き起こしてきた - ユースケースをJUnitのテストに書く - モデルがユースケースを満たせるか検証する - ユースケースに必要なふるまいが構造に追加される - さらにモデルが洗練されて意味のあるまとまり、新たな概念が発見される

Slide 18

Slide 18 text

Roleオブジェクト - DCIアーキテクチャ - https://digitalsoul.hatenadiary.org/entry/20100131/1264925022 - オブジェクト指向の本質が人間のメンタルモデルを捉えることにあるとした 上で、問題と解決方法を提示している - 問題は、構造を捉えることに長けている反面、ふるまいを捉えることが苦手 - 特定のふるまいをどのクラスにおくべきか - エンティティクラスが大量のメソッドで肥大化する - 今回は、 Factory Method について捉えた

Slide 19

Slide 19 text

Roleオブジェクト - グループチャットにメッセージを生成する Factory Methodを実装する - グループチャットは、メッセージを生成す るうえで、集約由来の生成ロジックを保持 しており、そのロジックを通してしか、 メッセージの生成を許可しない - グループチャットのふるまいとして持たせ ることは、良さそうに思える

Slide 20

Slide 20 text

Roleオブジェクト - グループチャットを通して生成する別の集 約のエンティティとして、ファイルやタス ク、ライブ… - グループチャットが持つFactoryとしての メソッドが増え、肥大化する

Slide 21

Slide 21 text

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 }

Slide 22

Slide 22 text

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) } }

Slide 23

Slide 23 text

働くをもっと楽しく、創造的に