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

Active Recordから考える次の10年を見据えた技術選定 / Architecture decision for the next 10 years at PIXTA

Yuichi Goto
September 16, 2021

Active Recordから考える次の10年を見据えた技術選定 / Architecture decision for the next 10 years at PIXTA

September 15, 2021 @ iCARE Dev Meetup #25

Yuichi Goto

September 16, 2021
Tweet

More Decks by Yuichi Goto

Other Decks in Programming

Transcript

  1. Active Record から考える
    次の 10 年を見据えた技術選定
    Yuichi Goto (@_yasaichi)
    September 15,2021 @ iCARE Dev Meetup #25

    View Slide

  2. 自己紹介
    Yuichi Goto(@_yasaichi)
    ピクスタ株式会社 執行役員 CTO
    兼 開発部長
    2020 年 7 月発売の「パーフェクト
    Ruby on Rails 【増補改訂版】」の
    共著者(Part 5 担当)
    2

    View Slide

  3. [PR] 会社の技術系 Podcast 「texta.fm」 を運営しています
    本発表の内容は、texta.fm でピクスタの
    技術顧問の和田卓人(@t_wada)さんと
    議論した内容が下敷きになっています。
    画像出典: https://anchor.fm/textafm 3

    View Slide

  4. 会社紹介
    ピクスタは本社と複数のグループ会社(ベトナム開発拠点含む)から構成
    されており、「クリエイティブ・プラットフォーム事業」を主要事業としている
    2015 年に東証マザーズ市場に上場し、2020 年 からはいわゆる第二創業
    期を迎えつつある状態
    本社社員 84 名のうち約 2 割がエンジニア職種であり(※)、全ての事業で
    Ruby on Rails(以下、Rails)を利用している
    RubyKaigi には
    過去 6 回協賛
    ※ これらの数値は全て 2021 年 6 月末時点のもの 4

    View Slide

  5. 本発表の背景と目的
    5

    View Slide

  6. 成長戦略の策定
    2020 年に今後 10 年の成長戦略を
    策定し、既存事業の周辺市場へ
    新規事業を展開することを決定
    (右図: 2021 年 12 月期第 2 四半
    期決算説明資料 [1] p. 47)
    CTO としてこの成長戦略の実現に
    向けた体制を作ることになった
    6

    View Slide

  7. どのように体制を考えていったか
    前ページの成長戦略を実現することを起点として、次のようにブレイクダウン
    して考えていった。以降では、各段階の結論を簡単に述べる。
    1. 成長戦略の実現にあたって必要な開発体制を考える
    2. 1 の体制の実現にあたって必要な構造を考える
    3. 2 の構造の実現にあたって必要な要素技術の選定方針を考える
    7

    View Slide

  8. 1. 開発体制のあるべき姿
    事業の成長段階によって次のように変化するビジネス上の方針に柔軟に
    適応できる開発体制 を作ることで、前述の戦略を実現できると考えた。
    PSF(※1)到達前: 試作品の提供を通じて顧客課題の解決策を探したい
    PMF(※2)到達前: 少人数で高速な仮説検証を回すことでプロダクトの
    最適な体験や価格を探したい
    PMF 到達後: 組織を拡大することで事業の成長を加速させたい
    ※1、※2 それぞれ「Problem/Solution Fit」「Product/Market Fit」の略。詳しくは [2] などを参照のこと 8

    View Slide

  9. 2. 理想の開発体制を実現する構造
    次の組織構造とシステムアーキテクチャによって、PMF 到達前後の方針の
    変化に適応できる開発体制を実現できると考えた。
    組織構造(※): 社内インフラ基盤を提供するプラットフォームチームが
    存在しており、新規プロダクトの立ち上げが開発チームだけで完結できる
    システムアーキテクチャ: フロントエンドとバックエンドが API スキーマに
    よって分離されており、後者を分割することで開発チームを拡大できる
    ※ 本発表では取り上げないが、詳細に興味のある方は texta.fm 「6. 1on1 in Public [3]」を参照のこと 9

    View Slide

  10. 事業の各段階におけるシステムアーキテクチャの変化
    ※ フロントエンドの分割による開発チームの拡大に関しては、まだ考えがまとまっていないため取り上げていない 10

    View Slide

  11. 3. 要素技術の選定方針
    本発表を通じて説明するいくつかの理由から、前述のシステムの要素技術を
    次の方針で選定することにした。
    フロントエンドとバックエンドの 開発言語を TypeScript で統一する
    フロントエンドでは Jamstack アーキテクチャを採用する
    バックエンドでは 最近話題の Prisma を見送り、アクティブレコードか
    データマッパーを実装した ORM を採用する
    ※ 本資料における「Prisma」は、特に断りがない限り ORM としてのそれを指すものとする 11

    View Slide

  12. 本発表の目的と得られるもの
    目的: 前ページの方針に至った理由を ORM を切り口に説明したうえで、
    ピクスタでの今後の取り組みを紹介する
    得られる(であろう)もの:
    ORM がバックエンドのアーキテクチャと開発生産性に与える影響
    技術と経営を繋ぐ技術選定の一例
    12

    View Slide

  13. 以降の構成
    第一部: Active Record から見る Rails
    第二部: Prisma から導かれるアーキテクチャ
    第三部: ピクスタでの今後の取り組み
    13

    View Slide

  14. 第一部: Active Record から見る Rails
    1. Web アプリケーションアーキテクチャ概論
    2. Active Record から見る Rails のアーキテクチャ
    3. なぜ Rails の利用を取りやめていく選択をしたか
    14

    View Slide

  15. Web アプリケーションアーキテクチャの主な論点
    Web アプリケーションでは、内部を適当数のレイヤーに分割してこれらを
    積み重ねる構造(※)によってその複雑さに対処することが多い [4]
    したがって、Web アプリケーションのアーキテクチャを考えるにあたって
    主な論点は次の 2 つとなる
    論点 1: どんな役割のレイヤーをどのように積み重ねるか
    論点 2: 各レイヤーをどのように設計するか
    ※ この構造を「レイヤードアーキテクチャ」と呼ぶ。厳密な定義は「POSA [5]」などを参照のこと 15

    View Slide

  16. 典型的なレイヤリング: 3 レイヤー
    One of the most common ways to modularize an
    information-rich program is to separate it into three
    broad layers: presentation (UI), domain logic (aka business
    logic), and data access. So you often see web applications
    divided into . . . On the whole Ive found this to be an
    effective form of modularization for many applications and
    one that I regularly use and encourage.
    ― Martin Fowler (2015) [6]


    ※ 太字強調は引用者によるもの 16

    View Slide

  17. 各レイヤーの名称と役割
    Fowler 氏の著書「Patterns of Enterprise Application Architecture [7]」
    (※1)では、3 つレイヤーの名称と役割は次のように定義されている。
    プレゼンテーション(※2): ユーザーからの HTTP リクエストなどを下位
    レイヤーの呼び出しへ変換する、ユーザーへ情報を表示する
    ドメイン: 入力データの妥当性確認、入力・格納データによる計算を行う
    データソース: データベースやメッセージングシステムなどと通信する
    ※1 以下、「PoEAA」と呼ぶ ※2 本レイヤーの設計方法は以降の議論とは関係しないため取り上げない 17

    View Slide

  18. これらのレイヤーの積み重ね方のパターン
    以降の説明では
    こちらを扱う
    ※ 「依存性逆転の原則」の詳細は書籍「Clean Architecture [8]」などを参照のこと 18

    View Slide

  19. ドメインレイヤーをどのように設計するか
    PoEAA で紹介されているパターンのうち、使用する言語・フレームワークに
    関係なく利用できるものは次の通り。
    トランザクションスクリプト: プレゼンテーションレイヤーからの 1 つの
    要求を処理する一連の手続き
    ドメインモデル: アプリケーションが対象とする問題領域(ドメイン)から
    抽出された概念をオブジェクトとして定義したもの
    19

    View Slide

  20. ドメインレイヤー内のサブレイヤー
    ドメインモデルを用いる場合、ドメインレイヤーは 2 つに分割されることが
    多く、うち上位のレイヤーを PoEAA では 「サービスレイヤー」と呼んでいる
    サービスレイヤーは複数のドメインモデルやデータソースを協調させて、
    プレゼンテーションレイヤーからの要求に応える役割を担う
    文脈によって同レイヤーを指す名称が変わるため注意が必要(e.g. 書籍
    「Clean Architecture [8]」の同心円図では「ユースケース」と呼ぶ)
    20

    View Slide

  21. データソースレイヤーをどのように設計するか
    PoEAA において、データストアが RDB の場合に利用できるパターンとして
    紹介されているものは次の通り。
    (テーブル|行)データゲートウェイ: RDB 内の(テーブル|レコード)への
    操作をカプセル化するオブジェクト
    アクティブレコード: ドメインロジックを実装した行データゲートウェイ
    データマッパー: ドメインモデルと RDB 内のレコードを相互変換するもの
    ※ Rails の Active Record との混同を避けるため、本発表では訳本のパターン名を意図的に用いている 21

    View Slide

  22. 2 つのレイヤーのアーキテクチャパターンの関係
    ドメインとデータソースのアーキテクチャパターンは互いに独立ではなく、
    次の組み合わせで利用することが多い(理由は後述)。
    A. トランザクションスクリプト + (テーブル|行)データゲートウェイ
    B. ドメインモデル + アクティブレコード
    C. ドメインモデル + データマッパー 以降の議論には関係しないため、
    こちらの組み合わせは紹介に留める
    22

    View Slide

  23. 各組み合わせの実装例
    次の正常系のフローからなる「ニュースレター登録機能」を、前ページの組み
    合わせ A と B で実装した例を示す。
    1. ユーザーは、フォームにメールアドレスを入力して「登録」ボタンを押す
    2. アプリケーションは、メールアドレスの形式が正しいことを確認する
    3. アプリケーションは、メールアドレスとその存在確認用のトークンを DB に
    記録して、ユーザーに確認メールを送信する
    23

    View Slide

  24. DB = Sequel.connect("connection_url")
    class SubscribeToNewsletter
    Result = Struct.new(:errors, :subscription_id, keyword_init: true)
    def self.call(email:)
    unless URI::MailTo::EMAIL_REGEXP.match?(email)
    return Result.new(errors: ["Email is invalid"])
    end
    values = { email: email, confirmation_token: SecureRandom.uuid }
    DB.transaction do
    id = DB[:newsletter_subscriptions].insert(values)
    EmailConfirmationMailer.deliver(values)
    Result.new(subscription_id: id)
    end
    rescue StandardError
    Result.new(errors: ["Something went wrong"])
    end
    end
    A. トランザクションスクリプト + テーブルデータゲートウェイ
    ドメインロジックは
    スクリプト内にべた書き
    24

    View Slide

  25. class SubscribeToNewsletterService
    Result = Struct.new(:errors, :subscription_id, keyword_init: true)
    def self.call(email:)
    unless URI::MailTo::EMAIL_REGEXP.match?(email)
    return Result.new(errors: ["Email is invalid"])
    end
    subscription = NewsletterSubscription.new(email: email)
    subscription.set_confrimation_token
    NewsletterSubscription.transaction do
    subscription.save!
    EmailConfirmationMailer
    .deliver(subscription.slice(:email, :confirmation_token))
    Result.new(subscription_id: subscription.id)
    end
    rescue StandardError
    Result.new(errors: ["Something went wrong"])
    end
    end
    B. ドメインモデル + アクティブレコード with サービスレイヤー
    25

    View Slide

  26. 「ニュースレター登録機能」の 2 つの実装例のアーキテクチャ上の違い
    26

    View Slide

  27. 第一部: Active Record から見る Rails
    1. Web アプリケーションアーキテクチャ概論
    2. Active Record から見る Rails のアーキテクチャ
    3. なぜ Rails の利用を取りやめていく選択をしたか
    27

    View Slide

  28. Rails の Active Record とは
    MVC パターンを採用している Rails におけるモデルに相当するもので、
    Rails を構成する gem の 1 つである activerecord にその実体がある [9]
    各アプリケーションでは、この gem が提供する ActiveRecord::Base という
    基底クラスを継承して固有のモデルを定義する
    その名称からも明らかなように、この基底クラスは PoEAA のアクティブ
    レコードの実装になっている [10]
    28

    View Slide

  29. PoEAA のアクティブレコードにはない機能
    基底クラス ActiveRecord::Base は PoEAA のアクティブレコードにはない次の
    代表的な機能を持っている。
    バリデーション: データベース操作の実行前にモデルの状態を検証できる
    ようにするもの(e.g. ある属性が空でないかの確認)
    コールバック: データベース操作の実行前後に任意のコードを実行できる
    ようにするもの
    設定したコードはデータベース操作と
    同一のトランザクション内で実行される
    29

    View Slide

  30. class NewsletterSubscription < ActiveRecord::Base
    validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
    before_create :set_confrimation_token
    after_create :send_email_confirmation_instructions
    private
    def set_confrimation_token
    self[:confirmation_token] = SecureRandom.uuid
    end
    def send_email_confirmation_instructions
    EmailConfirmationMailer.deliver(slice(:email, :confirmation_token))
    end
    end
    NewsletterSubscription.create(email: "[email protected]")
    2 つの機能を利用すると「ニュースレター登録機能」がモデルで完結する
    30

    View Slide

  31. 「ニュースレター登録機能」 における Rails のアーキテクチャ
    クラスとして実装されていた複数のレイヤーは
    Rails ではモデルとそのメソッドに対応する
    31

    View Slide

  32. Rails の Active Record が果たす役割
    次の方法で ドメインレイヤー以下をモデルクラスだけで構築できるように
    してレイヤーの数(≒ コードの記述量)を減らす ことで、高い開発生産性を
    実現している。
    アクティブレコードを採用して、ドメインレイヤーとデータソースレイヤーを
    1 つのクラスで実装できるようにした
    バリデーションとコールバックを導入して、サービスレイヤーのロジックを
    モデルクラスの DSL やインスタンスメソッドで実装できるようにした
    32

    View Slide

  33. Rails のアプローチの構造的な限界: 暗黙の 1:1 対応が崩れるとき
    プレゼンテーションレイヤーからのある要求に
    特化したモデル内のロジックが問題となる
    33

    View Slide

  34. 限界との向き合い方
    データベースレベル: イベントエンティティ(※1)を見落としていないか
    確認して、もしあれば対応するモデルに問題のロジックを移動する
    コードレベル(※2): 新たにサービスレイヤーを導入して、こちらに問題の
    ロジックを移動する
    アーキテクチャレベル(※2): ドメインを最初から小さくする、後から分割
    して小さくすることで問題を発生させない
    プレゼンテーションレイヤーからの要求と
    イベントエンティティの生成は 1:1 対応
    していることが多く、問題になりにくい
    ※1、※2 詳しくはイミュータブルデータモデル [11]、Ruby on Rails の正体と向き合い方 [12]をそれぞれ参照のこと 34

    View Slide

  35. 第一部: Active Record から見る Rails
    1. Web アプリケーションアーキテクチャ概論
    2. Active Record から見る Rails のアーキテクチャ
    3. なぜ Rails の利用を取りやめていく選択をしたか
    35

    View Slide

  36. 要因: 今後予想される新規事業の特性
    ピクスタは、「クリエイター基盤づくり」、「洗練された UI・UX」、「オンライン
    マーケティング(SEO・Web 広告)」の 3 つを強みとして掲げている [1]
    前述の成長戦略の実現にあたって、これらの 強みを活かした新規事業を
    展開することで、その成功確率を高めようとするのは自然な流れ である
    特に 後の 2 つの強みはフロントエンドと関係する ため、今後のシステム
    アーキテクチャの要素技術の選定にあたって考慮したい
    36

    View Slide

  37. 考慮すべき事項の一例: Core Web Vitals
    Google が提唱するユーザー体験の質を測定するための指標で、2020 年
    時点では LCP、FID、CLS の 3 つ(※)から構成される [13]
    2021 年 6 月から Google の検索結果順位を決める要因に加えられており
    [14]、検索流入に頼ることが多いピクスタでは無視できないものの 1 つ
    本指標で自然と高スコアを取れるシステムアーキテクチャにしておけば、
    SEO 関連の開発よりもプロダクトの仮説検証に多くの時間を割ける
    ※ それぞれ「Largest Contentful Paint」、「First Input Delay」、「Cumulative Layout Shift 」の略 37

    View Slide

  38. Rails の フロントエンドの今後
    Rails 7 に向けて、フロントエンドの開発生産性の向上に関する次の変更が
    行われたが(※)、ユーザー体験指標の向上に関する動きは見られない。
    デフォルトで Hotwire を導入して、SPA に近い体験の実装を容易にする
    Import maps を導入して、デフォルトで Webpacker を無効にする
    デフォルトで Sass のトランスパイルを無効にし、Tailwind CSS のような
    CSS フレームワークの利用を推奨する
    ※ 2021 年 9 月中旬時点のコミット内容に基づく記述で、正式リリースまでに変更される可能性がある 38

    View Slide

  39. Rails の出自を思い出すと、これは自然な流れである
    Basecamp と HEY はサービスの特性上
    どちらも SEO があまり問題にならない
    画像出典: Ruby on Rails の正体と向き合い方 [12] 39

    View Slide

  40. 2019 年時点での見解
    Active Record による Rails のバックエンドでの高い開発生産性は、その
    構造的な限界に対処さえできれば依然として魅力的である
    一方で Rails の出自を考えると、ピクスタで必要なユーザー体験指標の
    向上に注力する流れに自然となる可能性は高くない
    バックエンドは Rails のまま、ユーザー体験指標の向上に注力している
    フロントエンドのフレームワークを用いれば全て解決するのではないか
    40

    View Slide

  41. 新規事業 「PIXTA オンデマンド」 で Next.js を試験導入 (2020 年)
    バックエンドでは引き続き Rails
    (API モード)を利用している
    画像出典: https://od.pixta.jp 41

    View Slide

  42. 新たな問題と解決策
    背景: ピクスタでは、 PMF 到達前のチームを事業責任者、デザイナー、
    エンジニアの 3 人で構成することが多く、本新規事業も同様であった
    起きたこと: フロントエンドとバックエンドでの開発言語の違いから生じる
    認知負荷が想定以上に大きく、 担当エンジニアに負担をかけてしまった
    解決策: フロントエンドとバックエンドの 開発言語を統一することで、一人
    目のエンジニアにかかる認知負荷をできるだけ下げる
    42

    View Slide

  43. どのように開発言語を統一するか
    ピクスタでは、 MVP(※)を Web サービスとして製品化することが多いため、
    次の 2 つの方向性を検討したうえでより現実的な B 案を選択した。
    A. バックエンドでは引き続き Rails を利用し、ハイパフォーマンスな Web
    フロントエンドを Ruby で開発する方法を模索する
    B. フロントエンドでは Next.js のような Jamstack アーキテクチャの実装を
    利用し、Rails に近い生産性でバックエンドを JavaScript で開発する
    方法を模索する
    事業領域がクリエイティブ分野で
    あることに関連している(と思う)
    ※ 「Minimum Viable Product」の略。詳しくは「MVP の作り方 [15]」などを参照のこと 43

    View Slide

  44. 第一部完
    44

    View Slide

  45. 第二部: Prisma から導かれるアーキテクチャ
    1. Prisma がアーキテクチャ選定に与える影響
    2. なぜ Prisma の採用を見送る選択をしたか
    45

    View Slide

  46. 前提: JavaScript と TypeScript のどちらを選ぶか
    次のような良し悪しがあるものの、総合的に見て恩恵の方が大きいと考えて
    TypeScript を採用した。 以降の議論もこの決定を前提としている。
    良い点: システムが複雑化する PMF 到達後では静的型の恩恵が大きい。
    特にフロントエンドではテストが書きづらいため、より早くから必要になる
    悪い点: PMF 到達前の仮説検証において、静的型が素早い開発の妨げに
    なることがある(が、最終手段として any がある)
    46

    View Slide

  47. Prisma とは
    v1 は任意のデータストアに GraphQL を被せるサーバーの実装だったが、
    v2 (2020 年 6 月リリース [16])以降では TypeScript の ORM となった
    独自スキーマによるデータモデルの定義、定義に基づいた型安全なデータ
    ベース操作とマイグレーション機能が主な特徴として挙げられる
    調査したところ、 JavaScript の新興フルスタックフレームワーク(※)の
    多くで採用されていたため、まず初めに利用を検討した
    ※ 本発表では、2020 年前後に開発が始まったものを指している。具体的は、Blitz、RedwoodJS など 47

    View Slide

  48. const prisma = new PrismaClient();
    // Create
    const createdUser = await prisma.user.create({
    data: { email: '[email protected]' },
    });
    // Read
    const user = await prisma.user.findUnique({ where: { id: createdUser.id } });
    // Update
    const updatedUser = await prisma.user.update({
    where: { id: user!.id },
    data: { email: '[email protected]' },
    });
    // Delete
    await prisma.user.delete({ where: { id: updatedUser.id } });
    Prisma によるデータベース操作の例
    各データベース操作はRDB内の
    テーブルに対応するオブジェクトの
    メソッド呼び出しによって行う
    48

    View Slide

  49. Prisma は PoEAA のどのパターンを実装しているか
    公式ドキュメントでは、"Prisma is a new kind of Data Mapper ORM" と
    述べられている [17]
    前ページで示したように、Prisma は POJO(※)と RDB 内のレコードを
    相互変換しているので、広義のデータマッパーと言えなくもない
    しかし、最新の v3.0.2 時点でもこの POJO にドメインロジックを実装する
    方法を提供していないため、テーブルデータゲートウェイの実装と言った
    方が正しい
    ※ 「Plain Old JavaScript Object」の略。素の JavaScript オブジェクトのこと 49

    View Slide

  50. ドメインレイヤーのアーキテクチャ選定に与える影響
    ドメインレイヤー以下をドメインモデルと Prisma で構築しようとすると、
    Prisma の返す POJO のデータをドメインモデルに詰め直す必要がある
    特に Web アプリケーションの実装初期では、ドメインモデルと RDB 内の
    テーブルが 1:1 対応することが多いため、この作業は手間なだけである
    結果、データソースレイヤーに Prisma を用いると、 ドメインレイヤーでは
    自ずとトランザクションスクリプトを用いることになる
    p. 22 の理由の 1 つがこちら
    50

    View Slide

  51. import {Ctx} from "blitz"
    import db from "db"
    import {hashPassword} from "app/auth/auth-utils"
    import {SignupInput, SignupInputType} from "app/auth/validations"
    export default async function signup(input: SignupInputType, {session}: Ctx) {
    // This throws an error if input is invalid
    const {email, password} = SignupInput.parse(input)
    const hashedPassword = await hashPassword(password)
    const user = await db.user.create({
    data: {email: email.toLowerCase(), hashedPassword, role: "user"},
    select: {id: true, name: true, email: true, role: true},
    })
    await session.$create({userId: user.id})
    return user
    }
    実際にトランザクションスクリプトに帰着した例 (Blitz)
    出典: blitz/examples/custom-server/app/auth/mutations/signup.ts [18] 51

    View Slide

  52. 第二部: Prisma から導かれるアーキテクチャ
    1. Prisma がアーキテクチャ選定に与える影響
    2. なぜ Prisma の採用を見送る選択をしたか
    52

    View Slide

  53. 要因: トランザクションスクリプトの開発生産性に関する特性
    トランザクションスクリプトは ドメインが
    複雑になると開発生産性が急落する
    全期間の開発生産性はドメインモデルの
    方が高くなりうるため、Rails の生産性を
    再現する目的では Prisma は不向き
    画像出典: PoEAA(p. 29) 53

    View Slide

  54. 本件に対する Prisma 側の認識と姿勢
    Prisma 側 にも本件は認識されており、「Explore how to extend the
    Prisma Client [19]」という Issue では次のような趣旨が述べられている。
    Prisma で Computed field、Multi-step actions(※)、Custom
    Validation を実装する場合の指針を示せていない状況を改善したい
    上記のロジックをコントローラーに直接実装することは解決策の 1 つだが、
    アプリケーションが複雑になるとモデルによる抽象化が必要になる
    ※ 説明を読む限り、(アプリケーション|ドメイン)サービスに実装するようなロジックのことを指していると思われる 54

    View Slide

  55. 核心をついた Issue だが、直近 2 か月ほど動きがない状態
    2021 年 7 月 14 日に担当者が
    変更された後、特に動きなし
    画像出典: https://github.com/prisma/prisma/issues/7161 [19] 55

    View Slide

  56. Prisma 以外の選択肢
    アクティブレコードやデータマッパーの実装(※)を Rails と同じ限界に陥る
    ことを避けつつ用いる選択もあるが、次のように 一長一短で決め手に欠く。
    優れている点: ドメインレイヤーでは自ずとドメインモデルを用いることに
    なるため、ドメインが複雑になっても開発生産性が落ちにくくなる
    劣っている点: データモデルの定義に基づいてコードとその型を生成する
    Prisma には型安全性の面でどうしても及ばない(e.g. TypeORM [20])
    ※ Bookshelf.js や MikroORM、TypeORM、Sequelize などがこれに当たる 56

    View Slide

  57. 現時点での方針
    Rails に近い開発生産性を実現するという観点で見た場合、現時点では最も
    有力な TypeScript の ORM が存在しないため、次の方針をとることにした。
    アクティブレコードかデータマッパーの実装の中で相対的に型安全性が
    高いものを選んで、 その技術検証を進めていく
    並行して前述の Issue の行方を観察し、複雑なロジックを実装するための
    何らかの方法が考案された場合には Prisma へ移行する
    57

    View Slide

  58. 第二部完
    58

    View Slide

  59. 第三部:
    ピクスタでの今後の取り組み
    59

    View Slide

  60. ここまでのまとめ
    次の ピクスタのビジネス上の問題を解決するため、言語を TypeScript に
    統一し、Jamstack とアクティブレコードの実装を用いる方針を設定した。
    A. 成長戦略の実現のため、強みの 1 つであるオンラインマーケティングが
    活きる新規事業を展開し、その成功確率を高めたい
    B. 高速な仮説検証と SEO 関連の開発を両立させるため、開発生産性が
    高く、かつ Core Web Vitals で自然と高スコアが取れる構造を作りたい
    C. B の実現にあたってエンジニアにかかる認知負荷をできるだけ下げたい
    近年の流行を取り入れることを
    第一とした技術選定ではない
    60

    View Slide

  61. 現在検証中の技術スタック
    2021 年 8 月より、前述のシステムアーキテクチャと要素技術の選定方針を
    もとに、次の技術スタックの実現可能性を専任メンバーが検証している。
    フロントエンド: Apollo Client、GraphQL Code Generator、Next.js
    API スキーマ・ゲートウェイ: GraphQL、Apollo Gateway
    バックエンド: Apollo Server(with Express)、Nexus、TypeORM
    61

    View Slide

  62. 主な要素技術の選定理由
    Next.js: Jamstack アーキテクチャの実装の中でも最も進化が速いため
    GraphQL: Apollo Federation を利用することで、ほぼコードを書かずに
    API ゲートウェイ を実装できるため
    Nexus: NestJS と迷ったが、初期の学習コストの高さと持続可能性にやや
    懸念があり、まずは最小限の構成から始めることにしたため
    TypeORM: 機能、型安全性、持続可能性のバランスが取れていたため
    62

    View Slide

  63. 今後予定している進め方
    直近では新規プロダクトの開発は未定なため、まずは既存の PIXTA (※)
    サービスを前述のアーキテクチャに近づける取り組みを行い、学びを得る
    この PIXTA のリアーキテクティングは、数名の専任チームが開発チームと
    協力しながら 2 ~ 3 年かけて行う
    リアーキテクティングの成否はもちろんのこと、今後生じる新規プロダクト
    開発に得られた学びを還元することも同じくらい重視する
    ※ ピクスタが運営する写真・イラスト・動画・音楽のデジタル素材オンラインマーケットプレイスのこと 63

    View Slide

  64. リアーキテクティングの具体的な進め方 (現在検討中のもの)
    ※ Rails を GraphQL API のクライアントと見る考え方は github/graphql-client [21] から着想を得ている 64

    View Slide

  65. [PR] ピクスタでは、本取り組みに興味のある方を募集しています!
    画像出典: https://recruit.pixta.co.jp 65

    View Slide

  66. 第三部完
    66

    View Slide

  67. おわりに: 本発表で伝えたかったこと
    技術選定の一番の目的は流行を取り入れることではなく、ビジネス上の
    問題を解決することである(∴ 本発表でも後者の話題を多く取り上げた)
    トランザクションスクリプトとドメインモデルは、ドメインの複雑化に伴う
    開発生産性の変化の仕方が異なるため、その選定時には注意が必要
    ドメインとデータソースのアーキテクチャパターンは互いに関連するため、
    ORM の選定はバックエンドのアーキテクチャと開発生産性に強く影響する
    67

    View Slide

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

    View Slide

  69. 参考文献
    69

    View Slide

  70. 参考文献 1
    1. ピクスタ株式会社 "2021 年 12 月期第2四半期決算説明資料",URL: https://ssl4.eir-
    parts.net/doc/3416/ir_material_for_fiscal_ym/104956/00.pdf↩
    2. 馬田隆明 "PMF に到るまでのステージ別指針集 ",URL: https://speakerdeck.com/tumada/pmf-nidao-
    rumadefalsesutezibie-zhi-zhen-ji↩
    3. "6. 1on1 in Public by texta.fm",URL: https://anchor.fm/textafm/episodes/6--1on1-in-Public-e1078pn↩
    4. "Common web application architectures",URL: https://docs.microsoft.com/en-
    us/dotnet/architecture/modern-web-apps-azure/common-web-application-architectures↩
    5. Frank Buschmann,Regine Meunier,Hans Rohnert,Peter Sommerlad,and Michael Stal (1996) Pattern-
    Oriented Software Architecture,Volume 1,A System of Patterns: Wiley.↩
    6. Martin Fowler "PresentationDomainDataLayering",URL:
    https://martinfowler.com/bliki/PresentationDomainDataLayering.html↩
    7. Martin Fowler (2002) Patterns of Enterprise Application Architecture: Addison-Wesley Professional.↩
    70

    View Slide

  71. 参考文献 2
    8. Robert C.Martin (2018) Clean Architecture 達人に学ぶソフトウェアの構造と設計: KADOKAWA.↩
    9. "Welcome to Rails",URL: https://github.com/rails/rails/blob/6-1-stable/README.md↩
    10. "Active Record – Object-relational mapping in Rails",URL: https://github.com/rails/rails/tree/6-1-
    stable/activerecord#label-Philosophy↩
    11. kawasima "イミュータブルデータモデル",URL: https://scrapbox.io/kawasima/イミュータブルデータモデル↩
    12. Yuichi Goto "Ruby on Rails の正体と向き合い方",URL: https://speakerdeck.com/yasaichi/what-is-ruby-
    on-rails-and-how-to-deal-with-it↩
    13. "Introducing Web Vitals: essential metrics for a healthy site",URL:
    https://blog.chromium.org/2020/05/introducing-web-vitals-essential-metrics.html↩
    14. "More time, tools, and details on the page experience update",URL:
    https://developers.google.com/search/blog/2021/04/more-details-page-experience↩
    71

    View Slide

  72. 参考文献 3
    15. 馬田隆明 "MVP の作り方 とにかく雑に作る「手作業型 MVP」のススメ",URL:
    https://speakerdeck.com/tumada/mvp-falsezuo-rifang-tonikakuza-nizuo-ru-shou-zuo-ye-xing-mvp-
    falsesusume↩
    16. Nikolas Burk "Prisma 2.0: Confidence and productivity for your database",URL:
    https://www.prisma.io/blog/announcing-prisma-2-n0v98rzc8br1↩
    17. "Is Prisma an ORM?",URL: https://www.prisma.io/docs/concepts/overview/prisma-in-your-stack/is-
    prisma-an-orm↩
    18. "blitz/signup.ts at v0.40.0-canary.7 · blitz-js/blitz",URL: https://github.com/blitz-js/blitz/blob/v0.40.0-
    canary.7/examples/custom-server/app/auth/mutations/signup.ts↩
    19. "Explore how to extend the Prisma Client · Issue #7161 · prisma/prisma",URL:
    https://github.com/prisma/prisma/issues/7161↩
    20. "Prisma vs TypeORM",URL: https://www.prisma.io/docs/concepts/more/comparisons/prisma-and-
    typeorm#type-safety↩
    72

    View Slide

  73. 参考文献 4
    21. "github/graphql-client: A Ruby library for declaring, composing and executing GraphQL queries",URL:
    https://github.com/github/graphql-client↩
    This presentation is created by Marp. Great thanks @yhatt ! 73

    View Slide