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

Ruby on Railsの正体と向き合い方 / What is Ruby on Rails and how to deal with it?

Ruby on Railsの正体と向き合い方 / What is Ruby on Rails and how to deal with it?

Rails Developers Meetup 2019(2019/03/22 - 23)

Yuichi Goto

March 23, 2019
Tweet

More Decks by Yuichi Goto

Other Decks in Programming

Transcript

  1. Ruby on Railsの正体と向き合い方
    Yuichi Goto (@_yasaichi)
    March 23, 2019 @ Rails Developers Meetup 2019

    View Slide

  2. self.inspect
    Yuichi Goto
    @_yasaichi
    yasaichi
    ピクスタ株式会社 技術推進室 室長
    !2
    他社さんにおける技術基盤の
    ようなチームです

    View Slide

  3. !3
    Special thanks to @t_wada!
    本発表は、ピクスタの技術顧問である @t_wada

    (和田卓人)さんとの議論を通じて考えてきたことを
    まとめたものです。きっかけを頂き感謝しています。

    View Slide

  4. Agenda
    !4
    1. 背景と目的
    2. 第一部: Ruby on Railsの正体
    3. 第二部: Ruby on Railsとの向き合い方
    4. まとめ

    View Slide

  5. Rails Developers Meetup(Railsdm)と私
    2017/05 ⚫ Railsdm #1開催
    2017/12 ⚫ Railsdm 2017開催、LT枠で初登壇
    2018 Railsdm 2018 Day 1~4開催
    一参加者として楽しむ
    2019/03 ⚫ Railsdm 2019開催、2回目の登壇
    !5

    View Slide

  6. !6
    過去のRailsdmにおけるRailsとの「向き合い方」に関する発表(一部)
    ※ 各画像の出典は本発表資料の末尾に記載

    View Slide

  7. 過去発表の振り返りと所感
    • 前提: Railsは小〜中規模の開発に向いているが、どこかで限界が来る
    • 焦点: この限界に対して、コード・アーキテクチャレベルでどう対処するか
    • 所感:
    • 具体的な対処法(=向き合い方)に関しては多くの議論がされてきた
    • 「いつ、なぜ限界を迎えるのか?」という前提に関する議論はまだ少ない
    !7

    View Slide

  8. 本発表の目的とアプローチ
    • 目的:
    • 「Railsはいつ、なぜ限界を迎えるのか?」を明らかにする(「正体」編)
    • 明らかにした内容をもとに、既存の対処法を俯瞰的な視点から捉えて

    掲示することで、未来の有益な議論につなげる(「向き合い方」編)
    • アプローチ: DHH氏の過去のインタビューや著作の発言を起点とした考察
    !8

    View Slide

  9. Agenda
    !9
    1. 背景と目的
    2. 第一部: Ruby on Railsの正体
    3. 第二部: Ruby on Railsとの向き合い方
    4. まとめ

    View Slide

  10. 正体に迫る3つのキーワード
    !10
    DHHがRailsで目指したもの
    ② 早くてキレイ
    DHHがRailsで採ったアプローチ
    ③ 密結合
    Railsに働く大きな力学
    ① Basecamp

    View Slide

  11. キーワード①
    Basecamp
    Ø11

    View Slide

  12. Basecampとは
    • アメリカのシカゴに本社を置くBasecamp社(旧名37signals、DHHは

    同社のCTO)の提供するプロジェクト管理ツール [1]
    • 2003年、当時本業だったWebデザインのコンサルティング業務で起きて
    いたコミュニケーションの問題を解決するために開発した [2]
    • 当初は社内と既存顧客の間で利用していたが、「(顧客の)社内でも使い
    たい」という声を受け、2004年にサービスとして提供を始めた [2]
    !12

    View Slide

  13. DHHの関わり
    “Basecamp(注1)という新しいプロダクトの開発のとき,⾃分が開発
    環境を決められるようになり,それなら⼀番美しいソースコードを書け
    る⾔語にしようということでRubyにしたんです。”
    出典: 小飼弾のアルファギークに逢いたい♥ #2 (2006)
    “I did all the original programming for Base Camp”
    出典: Millionaire Story: David Heinemeier Hansson (2011)
    !13
    ※ 太字強調は引用者によるもの

    View Slide

  14. Railsとの関係
    “「Basecamp」と呼ぶ,Webベースのプロジェクト管理ツールを開発
    しました。Ruby on Railsはそのために開発したフレームワークで,最
    初は公開するつもりはなく,内部だけで使⽤していました。”
    出典: 「美しいコードを書けるからRubyを選んだ」

    ---Ruby on Rails作者David Heinemeier Hansson氏 (2006)
    “Railsを作ったのは、⾃分の仕事をより良く、より速くするためです。”
    出典: Ruby on Rails: DHHのインタビュー (2005)
    !14
    ※ 太字強調は引用者によるもの

    View Slide

  15. これらから言えること
    • DHHはBasecampと後にRailsとなるフレームワークを一人で作っていた
    • DHHはBasecampをより良く、より速く実装するためにRailsを作った
    !15
    当時のBasecampが置かれていた、「少人数のスタートアップでの
    プロダクト開発」という状況がRailsの設計に影響している

    View Slide

  16. キーワード②
    早くてキレイ
    Ø16

    View Slide

  17. DHHがRailsを作るまで
    1. Railsを作る前は、DHHは主にPHPとJavaを書いていた [3]
    2. Railsも最初PHPを使って作っていた(!)が、不満を感じていた [4]
    3. Martin FowlerやDave Thomasの書いた記事に影響されて、試しに
    Rubyを使ってみることを決める [5]
    4. 1週間ほどですっかりRubyにハマり、後のRailsを作り始める [5]
    !17

    View Slide

  18. DHHの問題意識
    “私はこの2つのソフトウェア開発⼿法に挟まれてたんですね。PHPに代
    表されるような「早いけど汚い」⼿法と、Javaに代表されるような「遅
    いけどキレイ」な⼿法にです。それで、両者を組み合わせたら、究極の
    ⽬標である「早くてキレイ」になるんじゃないかと思ったわけですよ。
    まあ、せめて、両⽅の⼈たちにアピールするくらいはできるんじゃない
    かなって。”
    出典: Ruby on Rails: DHHのインタビュー (2005)
    !18
    ※ 太字強調は引用者によるもの

    View Slide

  19. DHHがRailsで目指したもの
    “I was mostly doing PHP on my own, and I worked at a Java shop
    for a period of time. It was J EE to some extent and otherwise
    Java in general. Those were the two forming influences. With
    Ruby On Rails, I tried to form the best of both worlds to make it
    as quick as PHP and as solid and clean as something like Java.”
    出典: Rails creator on Java and other 'junk' (2007)
    !19
    ※ 太字強調は引用者によるもの

    View Slide

  20. 「早くてキレイ」は本当に実現できるのか?
    • ソフトウェアの開発速度と品質は(ある程度)トレードオフの関係にある
    • 原理的に解けない問題を解こうとする場合、何らかの仮定や制約を置いて
    対処することが多い
    !20
    「早くてキレイ」を実現するために、DHHも何かを妥協しているはず

    View Slide

  21. キーワード③
    密結合
    Ø21

    View Slide

  22. Railsがある程度妥協しているもの
    1. 柔軟さ
    • 例: CoC(Convention Over Configuration、「設定より規約」)
    • 柔軟さと引き換えに考える・書く量を減らしたことは、何度も話されてきた
    2. 疎結合(↔密結合)であること
    • 「Railsは密結合な設計だ」と言われても、ピンと来ない方もいるのでは
    !22
    ずっとRailsだけで仕事してきた方とか

    View Slide

  23. 密結合度合いを比較で理解する
    • 何らかの機能をRailsとHanamiでそれぞれ実装し、比較してみる
    • なぜHanami?
    • Hanamiはクリーンアーキテクチャをほぼそのまま実装している [6]
    • クリーンアーキテクチャは疎結合な設計の代表例のひとつである
    • どちらもRuby向けのWebフレームワークのため、設計の差が際立つ
    !23

    View Slide

  24. !24
    [脇道] そもそも、クリーンアーキテクチャとは?
    画像出典: Robert C. Martin "The Clean Architecture" (2012) [7]

    View Slide

  25. !25
    Enterprise Business Rules (例: Entity, Value Object)
    オブジェクト(や関数)の形でアプリケーションの

    ビジネスロジックをカプセル化する

    View Slide

  26. !26
    Application Business Rules (例: Interactor)
    Enterprise Business Rulesのオブジェクト群を
    使って各ユースケースの処理の流れを組み立てる

    View Slide

  27. !27
    Interface Adapters (例: Controller, Presenter, Repository)
    内側の円から外側(WebやDBなど)の円、または

    その逆方向へデータの形式をよしなに変換する

    View Slide

  28. 今までの説明で完全に理解できた

    はずなので、本題に戻ります
    Ø28

    View Slide

  29. お題: 簡易ユーザー登録機能
    正常系のフローは次の通り:
    1. ユーザーはメールアドレスを入力して送信ボタンを押す
    2. 送信されたメールアドレスの形式が正しいことを確認する
    3. アドレス存在確認用のトークンを作り、ユーザーにメールを送信する
    4. ユーザーをメールアドレス登録完了ページへリダイレクトする
    !29
    https://github.com/yasaichi-sandbox/user_registration

    View Slide

  30. !30
    Railsでの実装例: Controller
    class UserRegistrationsController < ApplicationController
    def create
    @user_registration = UserRegistration.new(user_registration_params)
    if @user_registration.save
    redirect_to complete_user_registrations_url
    else
    render :new
    end
    end
    private
    def user_registration_params
    params.require(:user_registration).permit(:email)
    end
    end

    View Slide

  31. !31
    Railsでの実装例: Model
    class UserRegistration < ApplicationRecord
    validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
    before_create :set_confirmation_token
    after_create :send_confirmation_instructions
    private
    def set_confirmation_token
    self.confirmation_token = SecureRandom.uuid
    end
    def send_confirmation_instructions
    UserRegistrationMailer.with(user_registration: self)
    .confirmation_instructions.deliver_now
    end
    end
    このへんは説明のためのコードなので、真似しないように

    View Slide

  32. !32
    Hanamiでの実装例: Controller
    module Web::Controllers::UserRegistrations
    class Create
    include Web::Action
    expose :error_messages
    def initialize(interactor: StartUserRegistration.new(
    mailer: Mailers::UserRegistrationConfirmation,
    repository: UserRegistrationRepository.new,
    token_generator: Utils::UrlSafeTokenGenerator
    ))
    @interactor = interactor
    end
    def call(params)
    result = @interactor.call(params[:user_registration])
    if result.successful?
    redirect_to routes.complete_user_registrations_url
    else
    @error_messages = result.errors
    self.status = 422
    end
    end
    end
    end
    メソッド呼び出しの対象がModelからInteractorへ

    View Slide

  33. !33
    Hanamiでの実装例: Interactor (入力値チェック)
    class StartUserRegistration
    include Hanami::Interactor
    class Validator
    include Hanami::Validations
    validations do
    required(:email).filled(:str?, format?: URI::MailTo::EMAIL_REGEXP)
    end
    end
    private
    def valid?(params)
    Validator.new(params).validate.yield_self do |result|
    result.messages.each_key { |key| error("#{key.capitalize} is
    invalid") }
    result.success?
    end
    end
    end
    #valid?がtrueを返すと#call(後述)が実行される

    View Slide

  34. !34
    Hanamiでの実装例: Interactor (処理本体)
    class StartUserRegistration
    include Hanami::Interactor
    def initialize(mailer:, repository:, token_generator:)
    @mailer = mailer
    @repository = repository
    @token_generator = token_generator
    end
    def call(params)
    @repository.transaction do
    user_registration = @repository.create(
    email: params[:email],
    confirmation_token: @token_generator.call
    )
    @mailer.deliver(user_registration: user_registration)
    end
    end
    end
    #before_create → #save → #after_createに相当

    View Slide

  35. Rails Model ≒ Hanami Interactor
    • お題のようなレコード作成+α時には、ModelはInteractorに相当する
    • Interactorの役割は、各ユースケースの処理の流れを組み立てること
    !35
    RailsのModelは特定のユースケースと密結合している

    View Slide

  36. Interactor化を支える3つの技術
    • Active Record(AR)パターンの適用: Modelとテーブルを1:1対応させ、
    ビジネスロジックの記述も許すことで、EntityとRepositoryの役割を実現
    • AR Validationsの発明: Modelの各属性が満たすべき条件を書く形で、
    Interactorでの入力値チェック相当の処理を実現
    • AR Callbacksの発明: ModelのCRUD操作に対するフックの形で、
    Interactorでのユースケースの処理組み立て相当の処理を実現
    !36
    C(R)UD操作とコールバックが自動で同一トランザクションになるのがキモ

    View Slide

  37. Clean ArchitectureとRails Modelの対応
    !37
    Clean Architecture Ruby on Rails
    ビジネスロジックの記述 Entity Model
    ユースケースの組み立て Interactor Model (AR Callbacks)
    入力値のバリデーション Interactor Model (AR Validations)
    DBアクセス・データ変換 Repository Model (AR Query Interface)

    View Slide

  38. ダメ押しの一手: RESTfulルーティング
    • ActiveRecordとそのValidations/Callbacksはv1.0時点で既に存在した
    • v1.2でRESTfulルーティング(resources)が導入され [8]、URLで表される
    リソースとModelが1:1対応した
    !38
    RESTfulのルーティングの導入で全てのコードがModelのCRUD

    操作を中心に整理されたことで、現在のRailsの原型が完成した

    View Slide

  39. 画像出典: ApplicationModel のある風景 (2018) [9]
    !39
    全てのコードがModelへのCRUD操作を中心に整理された状態
    R(Resource)-C-M間はRESTfulルーティングで1:1対応させる
    M-T(Table)間はActiveRecord
    パターンで1:1対応させる

    View Slide

  40. Ruby on Railsの正体
    • 次の2つの方法を組み合わせて、ModelのCRUD操作を中心にコードを

    整理して考える・書く量を減らすことで、「早くてキレイ」を実現した
    • RESTfulルーティングとActiveRecordパターンによって、URLで表される

    リソースからDB上のテーブルまでが密結合する構造を作った
    • ActiveRecordパターンとそのValidations/Callbacksによって、ビジネス
    ロジックとその組み立て処理を全てModelに書けるようにした
    !40

    View Slide

  41. 「密結合は悪」なのでは?
    • 大規模なアプリケーションの分業開発においては間違いなく避けるべき
    • 一方、DHHがRailsを作ったときに置かれていた状況は「少人数のスタート
    アップでのプロダクト開発」
    !41
    密結合にしても問題になりにくい状況だったので、これと引き換えに

    スタートアップでは最も重要な開発速度を出せるような設計にした

    View Slide

  42. 師曰く: Ruby on Railsの功績
    “少なくともスタートアップ企業にとってスピードは本当にクリティカル
    な⼒なので、もし密結合の状態でも速く⾛れるソフトウェアの構造があ
    るのであれば、それはゆっくり安定して継続的に歩いていく疎結合のソ
    フトウェア設計より強いということをRailsはある程度証明していたわ
    けですね。そしていま、その構造のまま⼤きくなるとすごく⼤変になる
    ということも証明している。”
    出典: マニアが潰したテスト駆動開発〜

    『健全なビジネスの継続的成長のためには健全なコードが必要だ』対談 (5) (2018)
    !42
    ※ 太字強調は原文のもの

    View Slide

  43. Railsはいつ、なぜ限界を迎えるのか?
    • いつ: あるModelが複数の異なるユースケースでC(R)UD操作される

    ようになったとき
    • なぜ: あるModelに書かれたValidations/Callbacksは特定のユース
    ケースと密結合しているため
    • 何が辛いのか: あるユースケースに特化したModelの中で、別のユース
    ケースの事情を考慮したコードを書かなければいけないこと
    !43

    View Slide

  44. 画像出典: ApplicationModel のある風景 (2018) [9]
    !44
    理想状態から離れ、限界が見え始めてくる状態

    View Slide

  45. 限界の表出の仕方
    1. 特定の条件でValidations/Callbacksを実行する or スキップする
    • 例: `if: :condition?`, `on: :context`, `save(validate: false)`
    • Modelに複数のユースケースの事情が詰め込まれていることを示す
    2. Controller内に`ApplicationRecord.transaction`を書く
    • ModelのCRUD操作を中心に処理が組み立てられなくなったことを示す
    !45

    View Slide

  46. 第一部完
    Ø46

    View Slide

  47. コーポレートサイトトップ: https://pixta.co.jp
    !47
    [PR] ピクスタはクリエイティブプラットフォームを創る会社です
    3つ全てのプラットフォームでRailsを利用。

    今年もRubyKaigiに協賛しています(6回目)

    View Slide

  48. 募集中の職種一覧: https://recruit.pixta.co.jp/careers
    !48
    [採用情報] 技術推進室のソフトウェアエンジニアを絶賛募集中

    View Slide

  49. Agenda
    !49
    1. 背景と目的
    2. 第一部: Ruby on Railsの正体
    3. 第二部: Ruby on Railsとの向き合い方
    4. まとめ

    View Slide

  50. 向き合い方は大きく2種類
    1. コードレベル
    これまで説明したRailsの限界を正面から解決しようとするアプローチ
    2. アーキテクチャレベル
    これまで説明したRailsの限界をそもそも回避しようとするアプローチ
    !50

    View Slide

  51. 過去に提案されたコードレベルでの向き合い方
    • ActiveRecordの分割(by @hanachin) [10]
    責務に応じてDB上の同一テーブルを参照する複数のModelを作る
    • Application Modelの導入(by @_h_s_) [9]
    DB上のテーブルに紐付かないController専用のModelを作る
    • Form/Service(その他様々なPORO)の導入 [11][12][13]
    Controllerからの単一メソッド呼び出しで処理一式を実行する層を作る
    !51

    View Slide

  52. 画像出典: ApplicationModel のある風景 (2018) [9]
    !52
    共通点: Controller or Actionと1:1対応する新しい層を導入する
    この層(Interactor相当)の作り方が違うだけ

    View Slide

  53. コードレベルでの基本方針
    • ビジネスロジックとその組み立て処理を全てModelに書けるようにして、
    「早くてキレイ」を実現したのがRails
    • Railsの辛さは複数のユースケースの事情がModelに集中することが原因
    !53
    ユースケース固有の処理を書く層を作り、これらの間で共通の処理
    (≒ビジネスロジック)だけをModelに書くようにする

    View Slide

  54. アーキテクチャレベルでの向き合い方
    • 基本方針: Railsの限界をコードレベルで解決しようとしない
    • オプション1: アプリケーションが対象とするドメインを小さくする
    • オプション2: 限界を迎える前に複数のサブシステムへ分割する
    • 組織構造とアーキテクチャは不可分なので、実行の際にはこれら両方の
    考慮が必要になることが多く、難易度は高い
    !54

    View Slide

  55. PIXTAサービスの現状と取り組み
    • 2011年6月にPerlからリプレイスして現在8年目(事業自体は13年目)
    • これまで説明した限界に直面しているため、次の2つの対応を行っている
    • 「本体」と呼ばれるリポジトリをこれ以上肥大化させないため、独立性の
    高い新機能は最初からサブシステムとして実装する
    • 「本体」からSoE(System of Engagement)を抽出して、巨大なSoR
    (System of Record)を任意の単位で分割できるようにする
    !55

    View Slide

  56. 事例1: 素材タイトルの多言語翻訳機能
    • 翻訳タイトルを素材のタグから自動生成していたのをやめるために開発
    • ドメインが小さく独立性が高いため、最初からサブシステムとして実装
    • I/O boundなアプリケーションだったこともあり、Node.jsを使って作った
    !56
    アプリケーションが対象とするドメインを小さくすると、Railsを選ぶ
    理由も少なくなるというジレンマがある

    View Slide

  57. 事例2: SoEの抽出(実施中)
    • 解決したいこと: 「本体」を複数のチームで開発しているため、組織が持つ
    本来の開発速度を発揮できていない
    • アプローチ: 「本体」を組織構造と一致するようにサブシステムに分割する
    • 問題点: Railsではフロントエンドとバックエンドもまた密結合しているため、
    このままの状態だとページ単位で分割せざるを得ない
    !57
    PIXTAではこの単位が組織の形とマッチしなかった(経験済み)

    View Slide

  58. 解決策: 先にSoEを抽出してから縦方向に分割する
    !58
    最初からSoE/SoRの構成なら縦方向の分割をすぐ始められるので、
    次の新規サービス開発(未定)ではBFF + Rails APIでやりたい
    Rails Monolith
    SoR

    (Rails API)
    SoE

    (Frontend + BFF)
    SoE

    (Frontend + BFF)

    Micro

    Frontends?
    任意の単位で分割可

    View Slide

  59. もうひとつの未来への道
    • とはいえ、成長中のプロダクトをひとつだけ持つ一般的なスタートアップで
    Railsの限界が見え始めたらすぐに分割を実施することは難しそう
    • 小規模での早さはそのままに、中規模を超えてもある程度走り続けられる
    フレームワークをRailsの上に作るのが現実的では?
    • Railsを構成する要素で作られている
    • 疎結合な設計に段階的に移行できる仕組みを持つ
    !59
    Laravelのサービスコンテナが参考になりそう

    View Slide

  60. Agenda
    !60
    1. 背景と目的
    2. 第一部: Ruby on Railsの正体
    3. 第二部: Ruby on Railsとの向き合い方
    4. まとめ

    View Slide

  61. まとめ: Ruby on Railsの正体
    • DHHが解決したかったこと: 少人数のスタートアップでのプロダクト開発で
    「早くてキレイ」を実現する
    • Railsのアプローチ: 次の2つの方法により、ModelのCRUD操作を中心に
    コードを整理して考える・書く量を減らす
    • URLで表されるリソースからDB上のテーブルまでを密結合させる
    • ビジネスロジックとその組み立て処理を全てModelに書けるようにする
    !61

    View Slide

  62. まとめ: Ruby on Railsの限界との向き合い方
    • Railsの限界: あるModelが複数の異なるユースケースでC(R)UD操作

    されるようになると、各々の事情がそのModelに集中して辛くなる
    • 限界との向き合い方:
    • コードレベル: ユースケース固有の処理を書くための層を導入する
    • アーキテクチャレベル: 対象のドメインを小さくするか途中で分割する
    !62

    View Slide

  63. 参考文献
    1. Basecamp, LLC "Basecamp: About our company", URL: https://basecamp.com/about
    2. Basecamp, LLC "A letter from the CEO", URL: https://basecamp.com/about/story
    3. IDG Communications, Inc. "Rails creator on Java and other 'junk'", URL: https://
    www.infoworld.com/article/2649156/rails-creator-on-java-and-other--junk-.html
    4. 角征典 "Ruby on Rails: DHHのインタビュー", URL: https://kdmsnr.com/translations/
    interview-with-dhh/
    5. "David Heinemeier Hansson interviewed by Randal Schwartz", URL: http://
    www.transcribed-interview.com/dhh-rails-david-heinemeier-hansson-interview-
    randal-schwartz-floss.html
    !63

    View Slide

  64. 参考文献
    6. "Architecture: Overview", URL: https://guides.hanamirb.org/architecture/overview/
    7. Robert C. Martin "The Clean Architecture", URL: https://blog.cleancoder.com/uncle-
    bob/2012/08/13/the-clean-architecture.html
    8. "Rails 1.2: REST admiration, HTTP lovefest, and UTF-8 celebrations", URL: https://
    weblog.rubyonrails.org/2007/1/19/rails-1-2-rest-admiration-http-lovefest-and-utf-8-
    celebrations/
    9. Hiroyasu Shimoyama "ApplicationModel のある風景", URL: https://speakerdeck.com/
    hshimoyama/rails-with-applicationmodel
    !64

    View Slide

  65. 参考文献
    10. Seiei Miyagi "ActiveRecordのモデルが1つだとつらい", URL: https://qiita.com/
    hanachin_/items/ba1dd93905567d88145c
    11. Bryan Helmkamp "7 Patterns to Refactor Fat ActiveRecord Models", URL: https://
    codeclimate.com/blog/7-ways-to-decompose-fat-activerecord-models/
    12. 諸橋恭介 "フォームオブジェクトとの向き合い方", URL: https://speakerdeck.com/moro/
    grow-form-objects-up
    13. Shinichi Maeshima "レールの伸ばし方", URL: https://speakerdeck.com/willnet/
    rerufalseshen-basifang
    !65

    View Slide

  66. ご清聴ありがとうございました
    Ø66

    View Slide

  67. Appendix
    Ø67

    View Slide

  68. 6ページ掲載の各資料について
    • [上段左] 参考文献13と同じ
    • [上段中央] "「Railsでまだ消耗しているの?」─僕らがRailsで戦い続ける理由─", URL: https://
    speakerdeck.com/toshimaru/why-we-use-ruby-on-rails
    • [上段右] 森久太郎 "Microservices Maturity Model on Rails", URL: https://speakerdeck.com/
    qsona/microservices-maturity-model-on-rails
    • [下段左] Tomohiro Hashidate "Realworld Domain Model on Rails", URL: https://
    speakerdeck.com/joker1007/realworld-domain-model-on-rails
    • [下段中央] 参考文献12と同じ
    • [下段右] 参考文献9と同じ
    !68

    View Slide