Version 1.0 (Scalaを用いた ドメイン駆動設計の 実践ノウハウ …Title...

80
Copyright © GREE, Inc. All Rights Reserved. (Scalaを用いた) ドメイン駆動設計の 実践ノウハウについて 2014/04/30 開発統括本部 シニアエンジニア 加藤 潤一 Version 1.0

Transcript of Version 1.0 (Scalaを用いた ドメイン駆動設計の 実践ノウハウ …Title...

  • Copyright © GREE, Inc. All Rights Reserved.

    (Scalaを用いた) ドメイン駆動設計の 実践ノウハウについて

    2014/04/30開発統括本部 シニアエンジニア

    加藤 潤一

    Version 1.0

  • Copyright © GREE, Inc. All Rights Reserved.

    • エンジニアとしての変遷 • 10歳: F-BASIC, N88-BASIC • 20歳: アセンブリ言語(x86), C/C++ • 30歳: Java, Seasar2, OSS, DDD • 40歳: DDD, Scala, Finagle/Trinity

    • 趣味 • 糖質制限+筋トレ

    自己紹介

    2

  • Copyright © GREE, Inc. All Rights Reserved.

    今日のテーマは

  • Copyright © GREE, Inc. All Rights Reserved.

    Scala と DDD

  • Copyright © GREE, Inc. All Rights Reserved.

    • オブジェクトやモデルの目的をおさらい

    • ペットショップのドメインを事例に、実践的にモデル駆動設計について解説

    • まとめ

    アジェンダ

    5

  • Copyright © GREE, Inc. All Rights Reserved.

    DDDの前に OOPの話し

  • Copyright © GREE, Inc. All Rights Reserved.

    オブジェクトってなんのために あるのか?

  • Copyright © GREE, Inc. All Rights Reserved.

    デザインパターンのためにあるのか?

    パターンはオブジェクトの再利用性を高めるための手段。パターンの前にオブジェクトがある。

    http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:State_Design_Pattern_UML_Class_Diagram.svg

  • Copyright © GREE, Inc. All Rights Reserved.

    よく見るオブジェクト例

  • Copyright © GREE, Inc. All Rights Reserved.

    全く間違いでは ないが何か違う

  • Copyright © GREE, Inc. All Rights Reserved.

    (本来的には) 概念を表すために オブジェクトがある

  • Copyright © GREE, Inc. All Rights Reserved.

    地点間の角度が正しいが 面積の正しさは保証しない

    http://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%AB%E3%82%AB%E3%83%88%E3%83%AB%E5%9B%B3%E6%B3%95

  • Copyright © GREE, Inc. All Rights Reserved.

    オブジェクトには 選び抜かれた 目的がある

  • Copyright © GREE, Inc. All Rights Reserved.

    ボクらがよくみる オブジェクト

  • Copyright © GREE, Inc. All Rights Reserved.

    Model

    View Controller

    処理を書くところとか説明されることが多いけど、それなら Script-View-Controller=SVCでよいはず。なぜモデルと呼ぶのか?

    Model-‐‑‒View-‐‑‒Controller  =  MVC

  • Copyright © GREE, Inc. All Rights Reserved.

    ユーザの頭の中にあるものとモデルを結びつける

    http://heim.ifi.uio.no/~trygver/themes/mvc/MVC-2006.gif

  • Copyright © GREE, Inc. All Rights Reserved.

    概念 モデル目的に合わせて 写し取る

    本来的にはここが出発点

    とはいえ、実装都合の問題は無視できない。どうするのか??

    モデル(オブジェクト)は 概念を写し取る手段

    DDDにおいては モデルはテーブルである

    とは言わない

  • Copyright © GREE, Inc. All Rights Reserved.

    ペットショップ でDDD

    https://flic.kr/p/5TEFth

  • Copyright © GREE, Inc. All Rights Reserved.

    ユビキタス言語 から

    考える(概念的)

  • Copyright © GREE, Inc. All Rights Reserved.

    そもそもどういうプロセスでやるか

    シナリオを選ぶ

    モデルを考えるモデルを実装する

    シナリオとはモデルの使い方を表したもの。基盤となるモデルがない場合はユーザストーリから考えてよい。

    いくつかのモデルを考える。良さそうなものを選ぶ。

    選んだモデルを実装してテストを書く。実装して使えるかどうか検証する。

  • Copyright © GREE, Inc. All Rights Reserved.

    ペットショップの日常から

    概念を引き出す

  • Copyright © GREE, Inc. All Rights Reserved.

    ‘顧客が商品を買う’ (この概念はペットショップ以外でもあるが…)

  • Copyright © GREE, Inc. All Rights Reserved.

    Item CategoryCustomer

    • 顧客(Customer)は、複数の商品(Item)を購⼊入する。  

    • 商品(Item)は、カテゴリ(Category)に所属する。

    1 * 1 1

    想定する最初のシナリオ

  • Copyright © GREE, Inc. All Rights Reserved.

    • ペットとペット以外の商品は同じように扱う。つまり、ペットは特別扱いしない

    • 商品は複数のカテゴリに所属しない。

    このモデルからわかること

    24

  • Copyright © GREE, Inc. All Rights Reserved.

    Item

    CategoryCustomer

    • 顧客(Customer)は、買い物かご(Cart)に商品(Item)を⼊入れたり出したりすることができる

    1

    *

    Cart

    1

    1

    1

    追加した新しいシナリオ

    1

  • Copyright © GREE, Inc. All Rights Reserved.

    • 顧客は直接商品と関連せず、カートを経由して間接的な関連を保持する。

    • 顧客とカートは1対1である(ほんとにそれでいいのか?)

    • カートは必要な時に都度作られる(実際と違う?カートはプールされているのでは?という議論をするかもしれない)

    このモデルからわかること

    26

  • Copyright © GREE, Inc. All Rights Reserved.

    Item

    CategoryCustomer

    • 顧客(Customer)は、買い物かご(Cart)にある商品(Item)を購⼊入する(Order)ことができる(つまり清算)

    1

    *

    Cart

    1

    1

    1

    Order1

    1

    * 1

    *

  • Copyright © GREE, Inc. All Rights Reserved.

    ドメインモデルの実装

  • Copyright © GREE, Inc. All Rights Reserved.

    エンティティの実装 (値そのものではなく、 アイデンティティが重要で

    見分けるもの)

  • Copyright © GREE, Inc. All Rights Reserved.

    Item Category

    Cart Order

    Customer

    登場したモデル群はエンティティ

  • Copyright © GREE, Inc. All Rights Reserved.

    • 商品ID • カテゴリID • 商品名 • 説明 • 価格

    エンティティにどんな属性があればいいか

    31

    Item

    Customer

    • 顧客ID • 顧客名 • 性別 • 郵便番号 • 都道府県名

    • 市区町村名 • 地番名 • 建物名

  • Copyright © GREE, Inc. All Rights Reserved.

    エンティティをサポートするトレイト

    {概念基準ではなく、実装上の都合で必要という意味が強い。このあたりは妥協点。

    絶対変更できない、識別子を保持する。

    ID型は何でもよい

  • Copyright © GREE, Inc. All Rights Reserved.

    顧客(Customer)を表すエンティティ

    属性が多すぎる。 エンティティの目的は同一性を保証することであって、属性を使って説明することではない。

    クラス名や属性名、振る舞いの名前は、ユビキタス言語で決めた言葉に従う。

  • Copyright © GREE, Inc. All Rights Reserved.

    商品(Item)を表すエンティティ

  • Copyright © GREE, Inc. All Rights Reserved.

    値オブジェクトの実装 (値だけが重要で、 識別しないもの)

  • Copyright © GREE, Inc. All Rights Reserved.

    属性を概念念単位で区切切って定義する

  • Copyright © GREE, Inc. All Rights Reserved.

    顧客のプロフィールを表す値オブジェクト

  • Copyright © GREE, Inc. All Rights Reserved.

    さらに住所や連絡先という概念念でまとめる

  • Copyright © GREE, Inc. All Rights Reserved.

    顧客ごとの設定を表す値オブジェクト

  • Copyright © GREE, Inc. All Rights Reserved.

    CustomerCustomerProfilePostalAddress

    Pref.Value

    Contact

    CustomerConfig

  • Copyright © GREE, Inc. All Rights Reserved.

    ドメインモデルの ライフサイクル (より技術的)

  • Copyright © GREE, Inc. All Rights Reserved.

    ライフサイクルの責務を分離

    42

    エンティティ (Global Entity) 集約(Aggregate)

    リポジトリ(Repository)

    ファクトリ(Factory)

    生成・永続化 を委譲

    エンティティや値オブジェクト内部で、ライフサイクル責務を負わない。

  • Copyright © GREE, Inc. All Rights Reserved.

    • 集約とは、複数のオブジェクトをひと塊として扱うためのオブジェクト

    • 集約であるグローバルエンティティを取得したり、保存するためのオブジェクト

    集約とリポジトリについて

    43

    集約 (Aggregate)

    リポジトリ (Repository)集約の保存

    集約の読込

  • Copyright © GREE, Inc. All Rights Reserved.

    集約(エンティティ)= テーブルではない それはエンコードの 一部でしかない

  • Copyright © GREE, Inc. All Rights Reserved.

    写像

    ファイルRDBMS

    Order

    Orderレコード

    OrderItremRecordOrderItremレコード

    バイト列JSON

    { …,“orderItems” : [ {…},{…}] }

    OrderItemOrderItemOrderItem

    リポジトリによる永続化

    乱暴な例ですが、OrderItemレコードを検索対象としない場合は、その属性をOrderレコードの属性として、JSONなどの文字列で格納するかもしれない。

  • Copyright © GREE, Inc. All Rights Reserved.

    リポジトリをサポートするトレイト

    エンティティの保存

    エンティティの検索

    エンティティの存在確認

    エンティティの削除

    Try[A]は 成功すると Success(value: A)、失敗するとFailure(ex: Exception)という値を表現する。

    コネクションやトランザクションを隠蔽するための暗黙的パラメータ。 immutableでは新しいリポジ

    トリインスタンス。mutableでは常にthisを返す。

  • Copyright © GREE, Inc. All Rights Reserved.

    Aggregate (Global Entity) Repository(I/F)

    リポジトリはDAOではありません

    JDBC版(実装)Memcached版(実装) API版(実装)

    DAO

    HashMap版(実装)

    遅いストレージに合わせてI/Fを設計する必要がある。Repository#resolveAll()はディスクを利用するストレージでは現実的ではないので、resolveAllOffsetWithLimit(…)などが必要。

  • Copyright © GREE, Inc. All Rights Reserved.

    エンティティ固有のリポジトリI/F

    CustomerRepository固有の検索メソッドを定義する。

    Repository(trait)

    CustomerRepository(trait) RepositoryOnJDBC(trait)

    RepositoryOnMemcacheded(trait)

    CustomerRepositoryOnJDBC(class) CustomerRepositoryOnMemcached(class)

  • Copyright © GREE, Inc. All Rights Reserved.

    • OnMemory(HashMap[ID, E]) • resolveAll(): List[E] • HashMap[ID, E]#toList メモリは高速であるという前提。

    • OnJDBC(SQL) • resolveAll(): List[E] • ディスクはメモリより超低速(100万~1000万倍ぐらい遅い) • “SELECT * FROM item” → あり得ない

    • resolveAllOffsetWithLimit(o, l): Seq[E] • “SELECT * FROM item OFFSET o LIMIT l” で limit で nを限定する

    • Listではなくcase class ChunkedEntities[E](entities: Seq[E], offset: Int, limit: Int)でもよい。

    • resolveByUserId: List[E] • “SELECT * FROM item WHERE user_id = ?” で user_id インデックスで絞り込める。

    エンティティの件数が大量の場合のI/F設計

    49

  • Copyright © GREE, Inc. All Rights Reserved.

    Aggregate (Global Entity)

    RepositoryOnJDBC

    Record DAO

    store resolve

    insertOrUpdate

    findBy

    DAOとRepositoryOnJDBCの関係1

    convertToRecord convertToEntity

    Domain

    Infrastructure

    インピーダンスミスマッチを解消

  • Copyright © GREE, Inc. All Rights Reserved.

    Aggregate (Global Entity)

    RepositoryOnJDBC

    DAO

    storeresolve

    DAOとRepositoryOnJDBCの関係2

    Domain

    Infrastructure

    クラスの数は減るが依存関係が密結合になりがち。
なるべく早くリリースしたい開発初期には有効かもしれない。

    Aggregate

  • Copyright © GREE, Inc. All Rights Reserved.

    ペットとペット以外の商品を区別したエンティティの例例

    Item

    Pet

    ItemImplPetImpl

    ItemRecord ItemDao

    ItemRepositoryOnJDBC

    ItemRepository

    ユビキタス言語において、Pet型はItem型の一部ととらえる場合、PetはItemの集合に含まれるので、ItemRepositoryから検索できることが自然。

    独自の振る舞い独自の属性

    implementstype

  • Copyright © GREE, Inc. All Rights Reserved.

    IDによる関節参照を利利⽤用する理理由

    外部の集約であるため、この集約には格納できない。外部の集約への関節的な参照を表す識別子を保持する。でもエンティティ本体が欲しい。

  • Copyright © GREE, Inc. All Rights Reserved.

    エンティティ本体を取得するための妥協案

    IDは関節参照であるので IDが示すエンティティが削除されている場合があるためTryで返す。

    副作用がなければよいとして、エンティティ本体を引き当てるためのリポジトリやコンテキストを渡す。

  • Copyright © GREE, Inc. All Rights Reserved.

    概念と実装を 掘り下げる

  • Copyright © GREE, Inc. All Rights Reserved.

    CartとOrder

  • Copyright © GREE, Inc. All Rights Reserved.

    買い物かご(Cart)をイメージする

    57

    https://flic.kr/p/63yDWh

    •買い物かご(Cart)には、買う予定の商品(CartItem)を入れたり出したりできる。

    •最後に清算できる。

  • Copyright © GREE, Inc. All Rights Reserved.

    買い物かごを表す(グローバル)エンティティ

  • Copyright © GREE, Inc. All Rights Reserved.

    買う予定の商品を表す(ローカル)エンティティ

  • Copyright © GREE, Inc. All Rights Reserved.

    シナリオに応じて振る舞いを考える

  • Copyright © GREE, Inc. All Rights Reserved.

    CartにItemを追加する副作用を起こす場合は新しいインスタンスを返す。Immutable前提

  • Copyright © GREE, Inc. All Rights Reserved.

    CartにItemを追加する

    ユビキタス言語に対応していること(英語なのがつらい)

  • Copyright © GREE, Inc. All Rights Reserved.

    OrderにItemを追加する

  • Copyright © GREE, Inc. All Rights Reserved.

    副作用がなければ、RepositoryやServiceを利用してもよい(個人的な考え)

    カートを清算する(Cartを基にOrderを⽣生成)

  • Copyright © GREE, Inc. All Rights Reserved.

    複雑な集約のI/O

  • Copyright © GREE, Inc. All Rights Reserved.

    Order(Global Entity)

    Repository

    Order Record

    Order DAO

    storeresolve

    insertOrUpdate

    findBy

    LEをテーブルにマッピングする例例

    Domain

    Infrastructure

    OrderItem Record

    OrderItem DAO

    insertOrUpdate

    findBy

    OrderItem(Local Entity)OrderItem(Local Entity)OrderItem(Local Entity)

    OrderItem Record

    OrderItem Record

    ドメインモデルはわかりやすくなる反面、リポジトリから下の層が複雑になる。覚悟が必要。

  • Copyright © GREE, Inc. All Rights Reserved.

    ドメイン層と 他の層の関係

  • Copyright © GREE, Inc. All Rights Reserved.

    レイヤーを意識識する

    Domain

    Application

    Infrastructure

    UI

    Item Category

    Cart Order

    Customer

    概念ばかりじゃアプリケーションは動作しない。 ドメインの下位層では、技術的な基盤を作り実現する。

    ドメインオブジェクト群に対する指揮系統

  • Copyright © GREE, Inc. All Rights Reserved.

    アプリケーション層

    ドメイン層

    インフラストラクチャ層

    具体的なパッケージ構成(⼀一例例)

    ドメイン層を隔離できれば、これ以外の方法でもよい

  • Copyright © GREE, Inc. All Rights Reserved.

    アプリケーションにドメイン層を 組み込む

  • Copyright © GREE, Inc. All Rights Reserved.

    Domain

    Application(Play2依存部分)

    Infrastructure

    ControllerJSON

    Dao/ORM

    Record JSON

    NOSQLClient

    Entity

    ValueObject Repository

    Factory

    Service

    HashMap

    ドメイン層は他の層から隔離離する

    ビジネスロジックと言われる振る舞いはドメインモデルが保持する。

    {Service

    リポジトリ、ファクトリはユビキタス言語とは結びつかない。ライフサイクルのみ

  • Copyright © GREE, Inc. All Rights Reserved.

    レイヤーおける サービスについて

  • Copyright © GREE, Inc. All Rights Reserved.

    Domain

    Application(Play2依存部分)

    Infrastructure

    ControllerJSON

    Entity

    VO Repository

    Factory

    Application Service

    いろいろなサービス

    何が違うのか?

    Infrastructure Service

    Domain Service

  • Copyright © GREE, Inc. All Rights Reserved.

    • Application Service • アプリケーション要件に対応する手続き(永続化が含まれることがある)。再利用性を高めるために実装することが多い。

    • 例:銀行取引履歴のエクセルエクスポート • Domain Service • 複雑なドメインオブジェクトの処理をメソッドにまとめたもの

    (永続化は含まれない)。実装の都合ではなく概念的な都合で作る。

    • 例:ある口座からある口座への送金手続き • Infrastructure Service • 汎用技術的な手続き • 例:メール送信やSQL発行など

    3つのサービスの違い

    74

  • Copyright © GREE, Inc. All Rights Reserved.

    アプリケーション層 のコード例

  • Copyright © GREE, Inc. All Rights Reserved.

    カートを作るアクション

    JSONを基にエンティティを生成し保存する。

  • Copyright © GREE, Inc. All Rights Reserved.

    カートに商品を追加するアクションドメインモデルはI/Oの責務を持たない。

    ビジネスロジック。複雑化する場合はドメインサービスを導

    入する。

  • Copyright © GREE, Inc. All Rights Reserved.

    • モデルを集約で表現すると概念としてわかりやすい反面、I/Oのボトルネックになりやすい。LEを遅延ロードさせたいなど、概念的な正しさを優先するのか、性能面を重視するのかのトレードオフ。後者の場合はLEをGEにすることもある。

    • 複数のテーブルにマッピングはつらい。コード生成の仕組みを実装する手もあるが、開発初期だけのコストなので投資しにくい。

    • デバイスの違いをできるだけ意識させないI/F • def store(tx: Transcation, entity: E): Try[(This, E)] ではない

    • 速いストレージ(メモリ)と遅いストレージ(ディスク)でI/F • DB版では def resolveAll は好ましくない

    メンタルモデルと実装の狭間で

    78

  • Copyright © GREE, Inc. All Rights Reserved.

    • 銀の弾丸はない。手間がかかる。 • まず、DDDの流儀をきちんと把握する

    (型を守る)ことが重要。 • 実践では、ドメインモデルと非機能要件の間にあるトレードオフに悩むことになる。原理主義に陥らずにいかに妥協するかが重要(型を知った上で型を破り離れること)

    まとめ

    79

  • Copyright © GREE, Inc. All Rights Reserved.