$30 off During Our Annual Pro Sale. View Details »

Realworld Domain Model on Rails

Realworld Domain Model on Rails

railsdm 2018の発表資料

Tomohiro Hashidate

March 25, 2018
Tweet

More Decks by Tomohiro Hashidate

Other Decks in Technology

Transcript

  1. Realworld Domain Model on Rails
    @joker1007

    View Slide

  2. self.inspect
    @joker1007
    Repro inc. CTO (
    要は色々やる人)
    Ruby/Rails
    fluentd/embulk
    RDB
    Docker/ECS (Fargate
    ェ……)
    Bigquery/EMR/Hive/Presto
    最近、kafka
    を触り始めました

    View Slide

  3. Rubykaigi 2018
    登壇予定
    TracePoint
    を中心にした黒魔術テクニックの話をする予定です。
    (tagomoris
    さんと共同発表)
    お楽しみに!

    View Slide

  4. Repro.inc
    について
    モバイルアプリケーションの行動トラッキング
    分析結果の提供と、それと連動したマーケティングの提供
    Ruby biz Grand prix 2017
    特別賞

    View Slide

  5. We are hiring
    Rails
    ごりごり書きたい人
    データ量の多いサービスに興味がある人
    DB
    パフォーマンスに敏感な人
    Bigquery
    やPresto
    等の分散クエリエンジンを触りたい人
    コード化されたインフラの快適度を更に上げたい人
    色々と仕事あります!お声がけください!

    View Slide

  6. 本題

    View Slide

  7. この話のテーマ
    継続してアプリケーションを改善し続けるために、より良い設計
    を考えるというのは大事なことだと思う。
    その中でも、DDD
    という考え方は今日かなりの影響力を持ってい
    るし、有用だと思う。
    Rails
    はWeb
    の開発効率に特化したフレームワークであり、現実的
    にはDDD
    と相性が悪い点が多々ある。
    一方で、Rails
    でかなり複雑なドメインを表現しなければならない
    ケースも増えてきている。
    Rails
    において、現実的でかつ複雑なアプリに耐えうるモデルの設
    計や表現方法を考えたい。

    View Slide

  8. Rails
    の基本構成要素
    Model
    View
    Helper
    Controller
    Job
    (
    他にも色々あるけど、省略)
    それぞれが何であり、どういう捉え方をするべきか改めて復習し
    ておこう

    View Slide

  9. Model
    アプリケーションロジックの本体。
    業務に関する概念を定義し振舞いを表現する。
    Rails
    において全ての中心。

    View Slide

  10. View
    アプリケーションの出力を表現する。
    出力先はHTTP
    である。
    HTML
    に限らず、JSON
    やCSV
    等の出力を表現するものはView
    であ
    る。

    View Slide

  11. Controller
    アプリケーションの入力に対するインターフェース。
    Rails
    では入力元はHTTP
    である。
    URL
    が各アクションのエントリポイントであり、フォームパラメ
    ータやJSON
    等を入力値とする。
    業務ロジックを表現するものではない。

    View Slide

  12. Job
    非同期処理実行のためのインターフェース。
    メッセージキューを入力として、処理の実行を開始する。
    入力元は異なるがController
    と同種の役割を持つ。
    Rails
    においては、キュー投入のインターフェースやメッセージ規
    約も兼ねる。

    View Slide

  13. オプショナルな構成要素
    これまで良く利用されてきた追加の構成レイヤー。
    Form
    Decorator / View Object
    Service

    View Slide

  14. Form
    Rails
    は基本がWeb
    なのでWeb
    からの入力を意識した名前が付けら
    れているが、実質的にはファクトリやオブジェクトビルダーと言
    える。
    ユーザからの入力データを元に複数のモデルや構造が複雑なモデ
    ルを生成する。
    入力に対するバリデーションも行う。
    永続化のためのインターフェースは持つが、処理自体は内部のモ
    デルに移譲する。
    実は、かなり重要な要素であると思う。詳しくは後述。

    View Slide

  15. Decorator / View Object
    表示のための加工処理をオブジェクト指向的に行うためのもの。
    Helper
    だとポリモーフィズムが難しい。
    どうしても分岐が増えてとっ散らかるので、それを避けるための
    レイヤー。

    View Slide

  16. Service
    業務上重要な振舞いそのものに名前を付けてクラスとして表現し
    たもの。
    複数のモデルオブジェクトに跨る処理を管理する。
    詳しくは 似非サービスクラスの殺し方 を参照

    View Slide

  17. まず大事なことは、それぞれの責任範囲
    を守ること
    そして、ちゃんと命名規約を守ること
    でないとフレームワークというものの利
    点を自ら潰すことになる

    View Slide

  18. DDD
    の概念とのマッピング
    直接の対応関係にある訳ではないが、Rails
    が基本的に持っている
    要素の中で近いと考えられるもの。
    エンティティ ­> Model (AR or not AR)
    値オブジェクト ­> Model (not AR)
    リポジトリ ­> ActiveRecord
    アプリケーションサービス ­> Controller
    ドメインサービス ­> Model (Service Class)

    View Slide

  19. 設計とは何か (
    私見)
    設計の半分以上は概念を発見して名前を付けることだと思う。
    基本的にアプリケーションの動作は、何かを入力して何かを出力
    するI/O
    の管理である。
    どこからデータが入ってきて、どういう名前の処理を経由してど
    ういう形式でどこに出力するか、それを決めるのが設計。
    (UI
    とかUX
    はまた別であると思うけど)

    View Slide

  20. コンテキストマッピング
    設計に必要なことは地図を作ること。
    責任範囲の境界にしっかり線を引き、それぞれの範囲に名前を付
    けられる様にする。
    各コンテキストは独立しており、通常は他のコンテキストを触ら
    ずその中で処理が完結する様にする。
    コンテキスト間で連携が発生する場合は、処理の流れを一方向に
    限定し、依存関係をコントロールする。

    View Slide

  21. 地図のブレイクダウン
    コンテキスト内部でも、原則として処理は一方向に進むことを維
    持する。
    複雑さのコントロールとして重要であり、読み易さにも大きく影
    響する。

    View Slide

  22. 責任の境界と集約ルート
    あるコンテキストの処理を実行する場合、境界の外から触ってい
    いエントリポイントを明確にしておく必要がある。
    そして、それ以外の場所から決して内側のオブジェクトを触って
    はいけない。
    集約はデータの整合性を守る単位である。

    View Slide

  23. 辛さの根源
    人間は複数のことを同時に考えるのに向いていな
    い。
    一連の処理そのものの複雑さや長さそのものはそこまで重要では
    ない。
    オブジェクト同士の関係性の地図が無いことが辛さを生む。

    View Slide

  24. 辛さの例
    境界が不明瞭でどこから処理が呼ばれるか分からない
    特にデータの流入元が複数あって把握が困難なケース
    依存関係が明確でないため、弄った時に一緒に壊れるクラス
    が多数ある
    一連の処理フローの中で各オブジェクトを行ったり来たりす

    一連の処理フローにおいて分岐の場所が散乱している

    View Slide

  25. 一度に考えれば良い範囲を限定する
    そのための責任境界と集約ルート

    View Slide

  26. Rails
    における集約の表現
    Rails
    で難しいのは、どのクラスが集約のルートに位置するかを明
    示すること。
    Ruby
    は可視性の制約が緩いので、触ってはいけない場所を強制し
    づらい。
    現実的には以下の様になると思う。
    ネームスペースを区切り、ネームスペースのルートを集約の
    ルートとする
    コメントやドキュメントで明示する

    View Slide

  27. ActiveRecord
    と集約の表現

    View Slide

  28. 開発初期
    開発初期はActiveRecord
    をそのまま利用するケースが多いと思
    う。
    AR
    のままでも集約境界とルートがコントロールできるなら問題な
    い。
    c
    l
    a
    s
    s O
    r
    d
    e
    r < A
    c
    t
    i
    v
    e
    R
    e
    c
    o
    r
    d
    :
    :
    B
    a
    s
    e
    h
    a
    s
    _
    m
    a
    n
    y :
    o
    r
    d
    e
    r
    _
    d
    e
    t
    a
    i
    l
    s
    e
    n
    d
    c
    l
    a
    s
    s O
    r
    d
    e
    r
    D
    e
    t
    a
    i
    l < A
    c
    t
    i
    v
    e
    R
    e
    c
    o
    r
    d
    :
    :
    B
    a
    s
    e
    b
    e
    l
    o
    n
    g
    s
    _
    t
    o :
    o
    r
    d
    e
    r
    e
    n
    d
    # O
    r
    d
    e
    r
    D
    e
    t
    a
    i
    l
    はO
    r
    d
    e
    r
    からしか触らないし生成されない

    View Slide

  29. Rails
    アプリの成長に伴う変化
    アプリが大きくなると、データベースのレコードは共通だが、コ
    ンテキストが異なるケースが出てくる。
    AR
    をロジックからインフラレイヤー寄りの役割に寄せて、レコー
    ドレベルの整合性だけを任せる様になる。
    コンテキスト毎に新しい集約クラスをPORO
    で定義し、整合性は
    そこでコントロールする。

    View Slide

  30. 再びForm
    オブジェクトの役割について
    複数のモデルに跨り、ケース毎の整合性を保ってAR
    のレコードを
    生成・更新するのがForm

    Rails
    においては、ビューに渡して表示を行うためのインターフェ
    ースも兼用する。
    実質的には、集約のルートに近いと言える。
    ただ、そうなるとForm
    という名前がフィットしないかも。

    View Slide

  31. Form
    オブジェクトのvalidation
    について
    validation
    をAR
    と二重でやるかについては好みがある。
    私はAR
    に移譲できるものはAR
    に任せて、errors
    を収集する方が良
    いと思う。
    G
    i
    f
    t
    F
    o
    r
    m = S
    t
    r
    u
    c
    t
    .
    n
    e
    w
    (
    :
    b
    u
    d
    g
    e
    t
    , :
    d
    e
    t
    a
    i
    l
    s
    , k
    e
    y
    w
    o
    r
    d
    _
    i
    n
    i
    t
    : t
    r
    u
    e
    i
    n
    c
    l
    u
    d
    e A
    c
    t
    i
    v
    e
    M
    o
    d
    e
    l
    :
    :
    V
    a
    l
    i
    d
    a
    t
    i
    o
    n
    s
    d
    e
    f v
    a
    l
    i
    d
    ?
    v
    a
    l
    i
    d
    i
    t
    y = @
    g
    i
    f
    t
    _
    d
    e
    t
    a
    i
    l
    s
    .
    a
    l
    l
    ?
    (
    &
    :
    v
    a
    l
    i
    d
    ?
    )
    @
    g
    i
    f
    t
    _
    d
    e
    t
    a
    i
    l
    s
    .
    e
    a
    c
    h
    _
    w
    i
    t
    h
    _
    i
    n
    d
    e
    x d
    o |
    g
    i
    f
    t
    _
    d
    e
    t
    a
    i
    l
    , i
    d
    x
    |
    g
    i
    f
    t
    _
    d
    e
    t
    a
    i
    l
    .
    e
    r
    r
    o
    r
    s
    .
    e
    a
    c
    h d
    o |
    a
    t
    t
    r
    , m
    e
    s
    s
    a
    g
    e
    |
    s
    e
    l
    f
    .
    e
    r
    r
    o
    r
    s
    .
    a
    d
    d
    (
    "
    g
    i
    f
    t
    _
    d
    e
    t
    a
    i
    l
    [
    #
    {
    i
    d
    x
    }
    ]
    "
    , m
    e
    s
    s
    a
    g
    e
    : m
    e
    s
    s
    a
    g
    e
    n
    d
    e
    n
    d
    e
    n
    d
    e
    n
    d

    View Slide

  32. 集約の子になるコレクションの表現
    Rails
    において、微妙に扱いが困るものがコレクション。
    ActiveRecord
    が変にクラスレベルでレコードコレクションを扱う
    ので、そこに色々定義しがちになる。
    scope
    を触るだけで済まなくなってきたら、コレクションクラスを
    自分で定義するか e
    x
    t
    e
    n
    d
    i
    n
    g
    等を使う。
    地味に厄介なのが、 h
    a
    s
    _
    m
    a
    n
    y
    に追加したら即永続化処理が動く
    点。

    View Slide

  33. クラスメソッドの弊害
    内包しているデータが無いので、引数でしかデータを渡せな
    い。
    クラスレベルからは基本的にpublic
    メソッドのみしか使えない
    (
    使わない)
    マルチスレッドで扱う時に危険 (sidekiq
    とか)
    これらの弊害に加えて、あるクラスの責任範囲が過剰に広くな
    る。
    実際には、もう一つ上のレイヤーで処理すべきことである。

    View Slide

  34. 集約ルートとの関係
    集約がコレクションを包含することは、よくある。
    一行単位のレコードと、集合全体の情報両方が必要な場合等。
    (ex,
    ページネーション、データ分析の結果を表示するケース)
    もしくは、集約ルートが肥大化するのを避けるために、コレクシ
    ョンを挟む等。

    View Slide

  35. 単純なコレクションクラスの例
    c
    l
    a
    s
    s R
    e
    t
    e
    n
    t
    i
    o
    n
    C
    o
    l
    l
    e
    c
    t
    i
    o
    n
    i
    n
    c
    l
    u
    d
    e E
    n
    u
    m
    e
    r
    a
    b
    l
    e
    d
    e
    f i
    n
    i
    t
    i
    a
    l
    i
    z
    e
    (
    b
    a
    s
    e
    _
    c
    o
    n
    v
    e
    r
    s
    i
    o
    n
    , c
    o
    n
    v
    e
    r
    s
    i
    o
    n
    s
    )
    @
    b
    a
    s
    e
    _
    c
    o
    n
    v
    e
    r
    s
    i
    o
    n = b
    a
    s
    e
    _
    c
    o
    n
    v
    e
    r
    s
    i
    o
    n
    @
    r
    e
    c
    o
    r
    d
    s = c
    o
    n
    v
    e
    r
    s
    i
    o
    n
    s
    .
    e
    a
    c
    h
    _
    w
    i
    t
    h
    _
    o
    b
    j
    e
    c
    t
    (
    {
    }
    ) d
    o |
    r
    , h
    |
    h
    [
    r
    .
    i
    d
    ] = r
    e
    n
    d
    e
    n
    d
    d
    e
    f e
    a
    c
    h
    (
    &
    b
    l
    o
    c
    k
    )
    ; @
    r
    e
    c
    o
    r
    d
    s
    .
    e
    a
    c
    h
    (
    &
    b
    l
    o
    c
    k
    )
    ; e
    n
    d
    d
    e
    f e
    a
    c
    h
    _
    w
    i
    t
    h
    _
    r
    e
    t
    e
    n
    t
    i
    o
    n
    _
    r
    a
    t
    e
    (
    &
    b
    l
    o
    c
    k
    )
    e
    a
    c
    h d
    o |
    _
    i
    d
    , r
    |
    y
    i
    e
    l
    d r
    , r
    .
    v
    a
    l
    u
    e
    .
    f
    d
    i
    v
    (
    @
    b
    a
    s
    e
    _
    c
    o
    n
    v
    e
    r
    s
    i
    o
    n
    .
    v
    a
    l
    u
    e
    )
    e
    n
    d
    e
    n
    d
    e
    n
    d

    View Slide

  36. ここまでのまとめ
    責任範囲の境界を明確にするための地図を作る
    境界の外から触っていいI/F
    を最小化する
    成長と共にAR
    を複数管理する集約ルートとしてのPORO
    を作

    集約ルートが肥大化しない様に、子になるオブジェクトに処理を
    移譲することを意識する。

    View Slide

  37. 単一のレコードレベルでの複雑さ
    個別のエンティティレベルの複雑さの根源は、状態管理の複雑さ
    だと思う。
    オブジェクトが不変であれば、インスタンスがあるか無いかしか
    知らなくていい。

    View Slide

  38. 状態管理の悪い例
    devise
    が駄目な例なので、それを元に話をする。
    (devise
    自体が駄目という話ではなく、サンプルとか使われ方が駄
    目という話)

    View Slide

  39. よくあるテーブル定義の例
    c
    r
    e
    a
    t
    e
    _
    t
    a
    b
    l
    e :
    u
    s
    e
    r
    s d
    o |
    t
    |
    t
    .
    s
    t
    r
    i
    n
    g :
    e
    m
    a
    i
    l
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    s
    t
    r
    i
    n
    g :
    n
    a
    m
    e
    t
    .
    s
    t
    r
    i
    n
    g :
    e
    n
    c
    r
    y
    p
    t
    e
    d
    _
    p
    a
    s
    s
    w
    o
    r
    d
    t
    .
    s
    t
    r
    i
    n
    g :
    c
    o
    n
    f
    i
    r
    m
    a
    t
    i
    o
    n
    _
    t
    o
    k
    e
    n
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    c
    o
    n
    f
    i
    r
    m
    e
    d
    _
    a
    t
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    c
    o
    n
    f
    i
    r
    m
    a
    t
    i
    o
    n
    _
    s
    e
    n
    t
    _
    a
    t
    t
    .
    s
    t
    r
    i
    n
    g :
    i
    n
    v
    i
    t
    a
    t
    i
    o
    n
    _
    t
    o
    k
    e
    n
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    i
    n
    v
    i
    t
    a
    t
    i
    o
    n
    _
    c
    r
    e
    a
    t
    e
    d
    _
    a
    t
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    i
    n
    v
    i
    t
    a
    t
    i
    o
    n
    _
    s
    e
    n
    t
    _
    a
    t
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    i
    n
    v
    i
    t
    a
    t
    i
    o
    n
    _
    a
    c
    c
    e
    p
    t
    e
    d
    _
    a
    t
    e
    n
    d

    View Slide

  40. 駄目な理由
    NULLABLE
    カラムの嵐になる (
    あったり無かったり)
    状態によって整合性を管理しなければならない範囲が変化す

    登録完了時には必須だが、confirm
    待ちや招待の承認前は
    不要
    他オブジェクトとの関連が、必須になったり初期化でき
    なかったりする
    自身の状態とどういう遷移を経てきたかを管理する必要がある
    それはそのまま、分岐の氾濫や整合性違反に繋がる

    View Slide

  41. 改善方法
    User
    、UserRegistration, UserInvitation
    に分割する

    View Slide

  42. User
    の例
    c
    r
    e
    a
    t
    e
    _
    t
    a
    b
    l
    e :
    u
    s
    e
    r
    s d
    o |
    t
    |
    t
    .
    s
    t
    r
    i
    n
    g :
    e
    m
    a
    i
    l
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    s
    t
    r
    i
    n
    g :
    n
    a
    m
    e
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    s
    t
    r
    i
    n
    g :
    e
    n
    c
    r
    y
    p
    t
    e
    d
    _
    p
    a
    s
    s
    w
    o
    r
    d
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    e
    n
    d
    c
    l
    a
    s
    s U
    s
    e
    r < A
    c
    t
    i
    v
    e
    R
    e
    c
    o
    r
    d
    :
    :
    B
    a
    s
    e
    v
    a
    l
    i
    d
    a
    t
    e
    s :
    e
    m
    a
    i
    l
    , p
    r
    e
    s
    e
    n
    c
    e
    : t
    r
    u
    e
    v
    a
    l
    i
    d
    a
    t
    e
    s :
    n
    a
    m
    e
    , p
    r
    e
    s
    e
    n
    c
    e
    : t
    r
    u
    e
    v
    a
    l
    i
    d
    a
    t
    e
    s :
    e
    n
    c
    r
    y
    p
    t
    e
    d
    _
    p
    a
    s
    s
    w
    o
    r
    d
    , p
    r
    e
    s
    e
    n
    c
    e
    : t
    r
    u
    e
    e
    n
    d

    View Slide

  43. UserRegistration
    の例
    c
    r
    e
    a
    t
    e
    _
    t
    a
    b
    l
    e :
    u
    s
    e
    r
    s d
    o |
    t
    |
    t
    .
    s
    t
    r
    i
    n
    g :
    e
    m
    a
    i
    l
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    s
    t
    r
    i
    n
    g :
    c
    o
    n
    f
    i
    r
    m
    a
    t
    i
    o
    n
    _
    t
    o
    k
    e
    n
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    c
    o
    n
    f
    i
    r
    m
    e
    d
    _
    a
    t
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    c
    o
    n
    f
    i
    r
    m
    a
    t
    i
    o
    n
    _
    s
    e
    n
    t
    _
    a
    t
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    e
    n
    d
    c
    l
    a
    s
    s U
    s
    e
    r
    R
    e
    g
    i
    s
    t
    r
    a
    t
    i
    o
    n < A
    c
    t
    i
    v
    e
    R
    e
    c
    o
    r
    d
    :
    :
    B
    a
    s
    e
    v
    a
    l
    i
    d
    a
    t
    e
    s :
    e
    m
    a
    i
    l
    , p
    r
    e
    s
    e
    n
    c
    e
    : t
    r
    u
    e
    v
    a
    l
    i
    d
    a
    t
    e
    s :
    c
    o
    n
    f
    i
    r
    m
    a
    t
    i
    o
    n
    _
    t
    o
    k
    e
    n
    , p
    r
    e
    s
    e
    n
    c
    e
    : t
    r
    u
    e
    e
    n
    d
    Form
    オブジェクトがUserRegistration
    の整合性を確認
    c
    o
    n
    f
    i
    r
    m
    e
    d
    _
    a
    t
    の記録とUser
    の永続化を行う

    View Slide

  44. UserInvitation
    の例
    c
    r
    e
    a
    t
    e
    _
    t
    a
    b
    l
    e :
    u
    s
    e
    r
    s d
    o |
    t
    |
    t
    .
    s
    t
    r
    i
    n
    g :
    e
    m
    a
    i
    l
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    s
    t
    r
    i
    n
    g :
    i
    n
    v
    i
    t
    a
    t
    i
    o
    n
    _
    t
    o
    k
    e
    n
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    i
    n
    v
    i
    t
    a
    t
    i
    o
    n
    _
    c
    r
    e
    a
    t
    e
    d
    _
    a
    t
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    i
    n
    v
    i
    t
    a
    t
    i
    o
    n
    _
    s
    e
    n
    t
    _
    a
    t
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    i
    n
    v
    i
    t
    a
    t
    i
    o
    n
    _
    a
    c
    c
    e
    p
    t
    e
    d
    _
    a
    t
    e
    n
    d
    UserRegistration
    と同様Form
    オブジェクトが整合性を確認
    i
    n
    v
    i
    t
    a
    t
    i
    o
    n
    _
    a
    c
    c
    e
    p
    t
    e
    d
    _
    a
    t
    の記録とUser
    の永続化を行う
    この時Form
    オブジェクトはUserRegistration
    の処理とは異なる

    View Slide

  45. どう変わったか
    Registration, Invitation
    はそれぞれ自分の中で2
    値の状態だけ管
    理すれば良い
    User
    はNOT NULL
    カラムonly
    になり、バリデーションが単純化
    する
    通知のコールバック等を定義する場所が明確になる
    User
    を利用する際に、登録に関するカラムやメソッドが邪魔
    にならない

    View Slide

  46. 正規化?
    この例はデータベースのテーブル設計が責任や状態管理の分離と
    上手く合致するが、常にそう上手くはいかない。
    Rails
    においては、どうやってもテーブル構造とモデル構造の癒着
    が避けられない。
    現実的には、どちらも意識して設計する必要がある。
    モデル設計とテーブル設計は別と書かれている書籍が多いが、
    Rails
    ではAR
    で表現しやすい形に落としこむのも重要。

    View Slide

  47. ちなみに弊社のテーブルは自分で駄目だと言っている例そのまん
    まです
    現実は色々あって厳しい……

    View Slide

  48. 不変を更に推し進める
    レコードの更新コストは結構高い。
    また、不変であれば分散処理との親和性が高くなりスケーラビリ
    ティの向上が見込める。
    レコード自体は不変に保ち、イベントを記録することで現在の状
    態を再現する。
    こうすることで追加のみでデータの更新を表現できる。
    イミュータブルデータモデルと呼ばれる表現方法。
    これをDDD
    に意識的に組込んだものが、ドメインイベントとイベ
    ントソーシングだと思う。
    (
    この辺り、成り立ちについての知識が無いため、私見です)

    View Slide

  49. Rails
    でのイベントの表現
    rails_event_store
    がかなり現実的に見える。
    ただ、私はこれをproduction
    で利用したことがないため、正しく理
    解している自信はない。
    あくまで参考程度として聞いて欲しい。

    View Slide

  50. イベントの発行
    c
    l
    a
    s
    s O
    r
    d
    e
    r
    P
    l
    a
    c
    e
    d < R
    a
    i
    l
    s
    E
    v
    e
    n
    t
    S
    t
    o
    r
    e
    :
    :
    E
    v
    e
    n
    t
    e
    n
    d
    s
    t
    r
    e
    a
    m
    _
    n
    a
    m
    e = "
    o
    r
    d
    e
    r
    _
    1
    "
    e
    v
    e
    n
    t = O
    r
    d
    e
    r
    P
    l
    a
    c
    e
    d
    .
    n
    e
    w
    (
    d
    a
    t
    a
    : {
    o
    r
    d
    e
    r
    _
    i
    d
    : 1
    ,
    o
    r
    d
    e
    r
    _
    d
    a
    t
    a
    : "
    s
    a
    m
    p
    l
    e
    "
    ,
    f
    e
    s
    t
    i
    v
    a
    l
    _
    i
    d
    : "
    b
    2
    d
    5
    0
    6
    f
    d
    ­
    4
    0
    9
    d
    ­
    4
    e
    c
    7
    ­
    b
    0
    2
    f
    ­
    c
    6
    d
    2
    2
    9
    5
    c
    7
    e
    d
    d
    "
    }
    )
    #
    p
    u
    b
    l
    i
    s
    h
    i
    n
    g a
    n e
    v
    e
    n
    t f
    o
    r a s
    p
    e
    c
    i
    f
    i
    c s
    t
    r
    e
    a
    m
    e
    v
    e
    n
    t
    _
    s
    t
    o
    r
    e
    .
    p
    u
    b
    l
    i
    s
    h
    _
    e
    v
    e
    n
    t
    (
    e
    v
    e
    n
    t
    , s
    t
    r
    e
    a
    m
    _
    n
    a
    m
    e
    : s
    t
    r
    e
    a
    m
    _
    n
    a
    m
    e
    )

    View Slide

  51. イベントの購読
    c
    l
    a
    s
    s I
    n
    v
    o
    i
    c
    e
    R
    e
    a
    d
    M
    o
    d
    e
    l
    d
    e
    f c
    a
    l
    l
    (
    e
    v
    e
    n
    t
    )
    # P
    r
    o
    c
    e
    s
    s a
    n e
    v
    e
    n
    t h
    e
    r
    e
    .
    e
    n
    d
    e
    n
    d
    s
    u
    b
    s
    c
    r
    i
    b
    e
    r = I
    n
    v
    o
    i
    c
    e
    R
    e
    a
    d
    M
    o
    d
    e
    l
    .
    n
    e
    w
    e
    v
    e
    n
    t
    _
    s
    t
    o
    r
    e
    .
    s
    u
    b
    s
    c
    r
    i
    b
    e
    (
    s
    u
    b
    s
    c
    r
    i
    b
    e
    r
    , t
    o
    : [
    I
    n
    v
    o
    i
    c
    e
    C
    r
    e
    a
    t
    e
    d
    , I
    n
    v
    o
    i
    c
    e

    View Slide

  52. AR
    のテーブル構造
    c
    r
    e
    a
    t
    e
    _
    t
    a
    b
    l
    e
    (
    :
    e
    v
    e
    n
    t
    _
    s
    t
    o
    r
    e
    _
    e
    v
    e
    n
    t
    s
    _
    i
    n
    _
    s
    t
    r
    e
    a
    m
    s
    , f
    o
    r
    c
    e
    : f
    a
    l
    s
    e
    )
    t
    .
    s
    t
    r
    i
    n
    g :
    s
    t
    r
    e
    a
    m
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    i
    n
    t
    e
    g
    e
    r :
    p
    o
    s
    i
    t
    i
    o
    n
    , n
    u
    l
    l
    : t
    r
    u
    e
    t
    .
    r
    e
    f
    e
    r
    e
    n
    c
    e
    s :
    e
    v
    e
    n
    t
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    , t
    y
    p
    e
    : :
    s
    t
    r
    i
    n
    g
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    c
    r
    e
    a
    t
    e
    d
    _
    a
    t
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    e
    n
    d
    a
    d
    d
    _
    i
    n
    d
    e
    x :
    e
    v
    e
    n
    t
    _
    s
    t
    o
    r
    e
    _
    e
    v
    e
    n
    t
    s
    _
    i
    n
    _
    s
    t
    r
    e
    a
    m
    s
    , [
    :
    s
    t
    r
    e
    a
    m
    , :
    p
    o
    s
    i
    t
    i
    o
    n
    a
    d
    d
    _
    i
    n
    d
    e
    x :
    e
    v
    e
    n
    t
    _
    s
    t
    o
    r
    e
    _
    e
    v
    e
    n
    t
    s
    _
    i
    n
    _
    s
    t
    r
    e
    a
    m
    s
    , [
    :
    c
    r
    e
    a
    t
    e
    d
    _
    a
    t
    ]
    a
    d
    d
    _
    i
    n
    d
    e
    x :
    e
    v
    e
    n
    t
    _
    s
    t
    o
    r
    e
    _
    e
    v
    e
    n
    t
    s
    _
    i
    n
    _
    s
    t
    r
    e
    a
    m
    s
    , [
    :
    s
    t
    r
    e
    a
    m
    , :
    e
    v
    e
    n
    t
    _
    i
    d
    c
    r
    e
    a
    t
    e
    _
    t
    a
    b
    l
    e
    (
    :
    e
    v
    e
    n
    t
    _
    s
    t
    o
    r
    e
    _
    e
    v
    e
    n
    t
    s
    , i
    d
    : f
    a
    l
    s
    e
    , f
    o
    r
    c
    e
    : f
    a
    l
    s
    e
    )
    t
    .
    s
    t
    r
    i
    n
    g :
    i
    d
    , l
    i
    m
    i
    t
    : 3
    6
    , p
    r
    i
    m
    a
    r
    y
    _
    k
    e
    y
    : t
    r
    u
    e
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    s
    t
    r
    i
    n
    g :
    e
    v
    e
    n
    t
    _
    t
    y
    p
    e
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    t
    e
    x
    t :
    m
    e
    t
    a
    d
    a
    t
    a
    t
    .
    t
    e
    x
    t :
    d
    a
    t
    a
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    t
    .
    d
    a
    t
    e
    t
    i
    m
    e :
    c
    r
    e
    a
    t
    e
    d
    _
    a
    t
    , n
    u
    l
    l
    : f
    a
    l
    s
    e
    e
    n
    d

    View Slide

  53. イベント駆動アーキテクチャの利点
    イベントをベースにすることで以下の様な利点が得られる
    任意の状態のオブジェクトを再現できる
    オブジェクトの更新理由が明確になり、再取得できる
    処理同士がイベントで繋がるため疎結合化が進む
    分散処理やマイクロサービスと親和性が高い

    View Slide

  54. イベント駆動アーキテクチャの不利な点
    一方で、以下の点が懸念材料となる
    読み込み時のコスト
    特にイベントが蓄積された後にどうするか
    トランザクションのコントロールが難しい
    ユーザーへの戻り値のコントロールが難しい
    今迄とは異なる概念でデータの流れを捉える必要がある
    メッセージングミドルウェアの運用管理
    純粋に技術的な難易度が上がるのでリスクも多い。

    View Slide

  55. まとめ

    View Slide

  56. 設計とは
    概念に名前を付けること
    地図を描くこと
    境界を決め、それを守ること

    View Slide

  57. 読み易く維持しやすいコードを書くため
    評価軸を明確に
    責任範囲を小さく
    状態を少なく
    依存を少なく
    オブジェクトの視点から見える世界を想像する
    多くの選択肢を知る

    View Slide

  58. そして最も難しいことは
    描いた地図を皆で共有すること。
    本当に難しい。
    共有できる地図を作るためにモデリング技法やユビキタス言語と
    いうものがある。
    (
    岸辺露伴 VS
    大柳賢 (
    ジャンケン小僧)
    を思い浮かべてください)

    View Slide

  59. 正直自分のことは棚上げした
    でないとこんな話中々できない
    それでも考えていきたいし
    より良い開発がしたいと思う

    View Slide

  60. 弊社で一緒に考えてくれる人を募集中

    View Slide

  61. ご静聴ありがとうございました

    View Slide

  62. View Slide