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

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

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

yoshiyoshifujii

February 16, 2020
Tweet

More Decks by yoshiyoshifujii

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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
    }

    View Slide

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

    View Slide

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

    View Slide