Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
PHPからGoへのマイグレーション for DMMアフィリエイト
Search
yabako kobayashi
December 12, 2024
Technology
2
760
PHPからGoへのマイグレーション for DMMアフィリエイト
DMM.go #9
DMMのアフィリエイト基盤をPHPからGoにマイグレーションした際の取り組みや、PHPerがGo開発で気をつけることについて
yabako kobayashi
December 12, 2024
Tweet
Share
More Decks by yabako kobayashi
See All by yabako kobayashi
オンプレからクラウドへ移行するための頑張らないアーキテクチャ
yabakokobayashi
0
94
Other Decks in Technology
See All in Technology
【内製開発Summit 2025】イオンスマートテクノロジーの内製化組織の作り方/In-house-development-summit-AST
aeonpeople
2
940
IoTシステム開発の複雑さを低減するための統合的アーキテクチャ
kentaro
1
120
Snowflake ML モデルを dbt データパイプラインに組み込む
estie
0
110
目標と時間軸 〜ベイビーステップでケイパビリティを高めよう〜
kakehashi
PRO
3
280
DeepSeekとは?何がいいの? - Databricksと学ぶDeepSeek! 〜これからのLLMに備えよ!〜
taka_aki
1
110
開発組織を進化させる!AWSで実践するチームトポロジー
iwamot
2
450
【5分でわかる】セーフィー エンジニア向け会社紹介
safie_recruit
0
19k
Apache Iceberg Case Study in LY Corporation
lycorptech_jp
PRO
0
340
AIエージェント開発のノウハウと課題
pharma_x_tech
5
3.6k
2/18 Making Security Scale: メルカリが考えるセキュリティ戦略 - Coincheck x LayerX x Mercari
jsonf
0
230
AIエージェント元年@日本生成AIユーザ会
shukob
1
230
わたしがEMとして入社した「最初の100日」の過ごし方 / EMConfJp2025
daiksy
14
5.2k
Featured
See All Featured
Statistics for Hackers
jakevdp
797
220k
Stop Working from a Prison Cell
hatefulcrawdad
267
20k
Art, The Web, and Tiny UX
lynnandtonic
298
20k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
49
2.3k
The Invisible Side of Design
smashingmag
299
50k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
129
19k
The Straight Up "How To Draw Better" Workshop
denniskardys
232
140k
How STYLIGHT went responsive
nonsquared
98
5.4k
A Modern Web Designer's Workflow
chriscoyier
693
190k
Transcript
© DMM.com PHPからGoへの マイグレーション for DMMアフィリエイト 1
© DMM.com 自己紹介 小林 将太 ➢ 2022/11 DMM入社 ➢ マーケティングテクノロジー部所属
➢ DMMアフィリエイトの開発・運用をして ます 2
© DMM.com DMMアフィリエイトを(一部)リプレイスしました! 本日の内容 Before After 3
© DMM.com 目次 1. DMMアフィリエイトの概要 2. リプレイスの概要 3. Goでの開発 4.
Goを採用したメリット 4
© DMM.com DMMアフィリエイトの概要 5
© DMM.com 既存のアーキテクチャ PHPフレームワークを使用したシンプルな構成 • 複数の画面 ◦ アフィリエイターが閲覧する画面 ◦ 管理者が操作する画面
• 定期実行されるバッチ処理 • クリック/購入を登録するインターフェース • etc… これらが同じリポジトリに詰め込まれたモノリシックな構成 6
© DMM.com リプレイスの概要 7
© DMM.com リプレイス後アーキテクチャ 工数や影響を考慮して段階的なリプレイスを採用 • ユーザーの画面を対象 ◦ 全ての画面を一気にリプレイスせず、変更後の 影響範囲を狭める •
フロントとバックエンドは分離 ◦ 既存は密結合している ◦ 規模や報酬を扱うことを鑑みると、分離してス ケーラビリティや変更影響を限定的にしたい • DBはオンプレミスをそのまま使用 ◦ 報酬(金銭)の取引があるため個人情報が保存 されており、クラウド移行に大きな課題がある 8
© DMM.com Go採用のモチベーション ❏ 部署内で言語統一の流れがあった ❏ Go言語への統一プロジェクトで進化を遂げる、マーケティングテクノロジー部の決意 移管されてきたプロダクト 各プロダクトごとに開発言語や環境が異なり、 相互に連携してるので複雑度が高い
部署の課題 古いシステムが多い 属人化が激しく、メンテナンス性も著しく損 なっているので保守工数が増大 Goへの言語統一 • システム全体の一貫性の向上 • エンジニアリソースを流動的に配 置 生産性/メンテ ナンス性UP 課題へのアプローチ 9
© DMM.com アフィリエイトにGoの採用はフィット するのか? 10
© DMM.com アフィリエイトに対するGoの合致度 1. パフォーマンス観点 a. データ量 i. いままで発生した報酬履歴 1.
レポーティングなどで使用 ii. 未来に支払いする報酬の計算 b. 負荷 i. 動画などキャンペーン時にはアフィリエイトを通した大量リクエスト 1. 大体300rpsぐらい 2. スケーラビリティ(並行処理) a. リアルタイムデータの集計 b. レポートの生成 11 Goのイメージ ❏ パフォーマンスが高い ❏ コンパイルされたバイナリが直接実行されるため、レイテンシが格段に少ない ❏ メモリ管理が効率的 ❏ スケーラビリティに優位性がある ❏ 軽量なゴルーチン(並行処理)が簡単に使える ❏ シンプルな構造と明確な依存管理 アフィリエイト特性に当てはめて検討 Goにマッチしてそう
© DMM.com Goでの開発 12
© DMM.com Go開発の取り組み(プロジェクト構成) 13 課題1. 記述量増加 // Item構造体を定義 type Item
struct { ID int `json:"id"` Name string `json:"name"` Price float64 `json:"price"` } // 指定IDのアイテムを取得する関数 func getItem(w http.ResponseWriter, r *http.Request) { id := r.URL.Query().Get("id") var item Item err := db.QueryRow("SELECT id, name, price FROM items WHERE id = $1", id).Scan(&item.ID, &item.Name, &item.Price) if err != nil { http.Error(w, "Item not found", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(item) } // 指定IDのアイテムを取得する関数 function getItem($id) { global $pdo; $stmt = $pdo->prepare("SELECT id, name, price FROM items WHERE id = ?"); $stmt->execute([$id]); return $stmt->fetch(PDO::FETCH_ASSOC); } // APIの呼び出し処理 header('Content-Type: application/json'); echo json_encode(getItem($_GET['id'])); Go PHP
© DMM.com なるべく考えることを減らして コアなビジネスロジックに注力したい 14
© DMM.com Go開発の取り組み(プロジェクト構成) 15 1. フロントとバックエンドのインターフェース共通化 ❏ OpenAPI定義を元にコードを生成 ❏ 使用ツール
❏ Backend: oapi-codegen ❏ Frontend: swagger-typescript-api ❏ OpenAPI定義とRes/Req構造体を見比べる必 要がなくなった ❏ レビュー負荷軽減 各リポジトリにサブモジュールとして配置
© DMM.com Go開発の取り組み(プロジェクト構成) 16 2. DB操作はSQLによったものを選定 ❏ SQLから生成できるsqlc ❏ SQLクエリの書き方さえ知ってればよいので
ORMのメソッドを調べたり、メソッドで表現できない複雑 なクエリとで書き分ける必要がない ❏ SQLはメンバー全員慣れていたので好都合 →しかしテーブル依存で断念。。。 ❏ 主な敗因 ❏ さまざまなシナリオをカバーするために複数の類似クエリを作成することが多い ❏ 構造体がNULLになりえるのにNOT NULL型で生成される(#1208) CREATE TABLE account ( id INTEGER PRIMARY KEY, name VARCHAR(128) NOT NULL, email TEXT ); SELECT email, (SELECT a.id FROM account a WHERE a.name = 'foo' LIMIT 1) AS "id" FROM account; type TestRow struct { Email sql.NullString ID int32 // 本当は sql.NullInt32 になってほし い! } 例:
© DMM.com Go開発の取り組み(プロジェクト構成) 17 2. DB操作はSQLによったものを選定 (2回目) ❏ SQLファーストなORMを採用 ❏
bun ❏ クエリビルダーの感覚で使用できる ❏ 機能が少ないので、なにを使うかで迷うことがない デメリット ❏ インデックスヒントやオプティマイザヒントは提供されてないので、必要なプロダクトの場合相性が 良くない ❏ 有名なJSランタイムの名前と被っているのでググラビリティがすこぶる悪い
© DMM.com Go開発の取り組み(プロジェクト構成) 18 自動生成するなら goaという選択肢もあるのでは? ❏ まずgoaを採用していても大きな問題になるような構成ではなかった ❏ 今回に関しては既存の知識で解決できたので、追加の学習の観点で採用しなかった
❏ goaのDSLについて知る必要がある ❏ OpenAPI定義とSQLは広く知られている ❏ goa genにカスタマイズするみたいなパターンが出てきた時や、トラブルシュートに不安があった ❏ DMMではgoaは広く採用されているが、部署内では採用事例がなかった
© DMM.com Go開発の取り組み(プロジェクト構成) 19 課題2. レイヤー分割 ❏ クリーンアーキテクチャを採用 ❏ Goのフレームワークには
LaravelやCakePHP のようなフルスタックフレームワークのお決まり が無いことが度々ある ❏ 今後は規模が大きくなる想定だったので、責務 はある程度分割したかった ❏ ベースとなるルールを用意したかった ├── app/ │ ├── domain/ │ │ ├── model/ │ │ │ └── account.go │ │ ├── repository/ │ │ │ └── account.go │ │ └── service/ │ │ └── account.go │ ├── infra/ │ │ ├── dao/ │ │ │ └── account.go │ │ └── external/ │ │ └── db │ ├── interface/ │ │ └── account.go │ └── usecase/ │ └── account.go └── cmd/ └── main.go
© DMM.com Go開発の取り組み(プロジェクト構成) 20 type Account struct { ID uint
Email string Name string Created time.Time Modified time.Time } 最初はdomain/modelとテーブルが1対1で作成していた ❏ 開発初期は使用テーブルも少な かったので、不都合が発生しな かった ❏ 既存フレームワークもmodelが テーブル同等だったので、なんら 問題がないように思えた 課題2. レイヤー分割
© DMM.com 開発中期あたりから目に見えて破綻の足音が ・・・ ❏ 複数のモデルにまたがるロジックが出 来上がる ❏ アプリケーションレイヤにビジネスロ ジックが露出しだす
❏ そもそも既存テーブルを使い回す関 係上、テーブル設計からできないので 負債が表出してしまっている 21
© DMM.com Go開発の取り組み(プロジェクト構成) 22 課題2. テーブル構造の負債 ❏ Domain Modelとテーブル操作用の structを分割
❏ Domain Modelとテーブル操作後の変換層を追加 ├── domain/ │ ├── model/ │ │ ├── account.go │ │ └── reward.go │ └── repository/ │ ├── account.go │ └── reward.go └── infra/ ├── adapter/ │ ├── account.go │ └── reward.go └── persistence/ └── entity/ ├── account.go ├── reward.go ├── price.go └── conversion.go repository adapter entity model dao use implement
© DMM.com Go開発の取り組み(プロジェクト構成) 23 課題2. テーブル構造の負債 ❏ Model側はアプリケーションレイヤ が欲しいフィールドとビジネスロ ジックに集中できる。
❏ 逆にadapter分、記述量は増えた ので、その点は嬉しくはない・・・
© DMM.com PHP開発者がハマるGoの開発 24 静的型付け ❏ PHPだと配列やオブジェクトに様々な値を、動的に詰め込んで後で取り出すといったことが可能 なので最初は使いづらく感じる ❏ 慣れているmapを使いがちになるので注意する
❏ フィールドの型や意味が固定されている場合は構造体を作る Go PHP
© DMM.com PHP開発者がハマるGoの開発 25 レシーバー /メソッド ❏ DIと合わせて「どうなってるか分かりづらい」と言われることあり ❏ 「クラスを定義する」という発想を捨て、「構造体に機能を持たせる」という考え方に切り替える方が理
解してもらいやすい コンストラクタが独立しているかつ関数 名が決まってない PHP構文との違いだがclass(struct)の 中で宣言されていないので、メソッドな のが直感的でない
© DMM.com PHP開発者がハマるGoの開発 26 グローバル変数 ❏ PHPではグローバル変数をリクエスト毎の共有として使ってしまったりする ❏ 例えば、セッション情報をグローバル変数に格納して、どの処理でもアクセス可能にする //
PHPでのグローバル変数使用例 $_SESSION['user'] = $user; ❏ Goでグローバル変数を使うと... ❏ スレッドセーフではないため、データが競合し、アプリケーションが不安定になることがある ❏ 場合によってはOOMが発生する ❏ Goではcontextでリクエスト毎の情報伝搬を行なう
© DMM.com Goを採用したメリット 27
© DMM.com コンテナサービスとの親和性 28 イメージサイズが小さい PHP(laravel) 578MB Go(echo) 15.3MB ❏
イメージサイズが小さいのでpullが早くなる ❏ レジストリ管理コストが微量だけど削減 ❏ ストレージ料金
© DMM.com コンテナサービスとの親和性 29 スピンアップが早い ❏ Goはシングルバイナリで動くので、単純に起動からレスポンス受付が早い ❏ パフォーマンスがPHPが低いというわけではない PHP
GO 0.000766s 0.000831s ある設定をしたレスポンス速度比較 単純なAPIで計測した結果
© DMM.com コンテナサービスとの親和性 30 スピンアップが早い ❏ PHPにも動的コンパイルをキャッシュする仕組みがあり、これを利用することで実行速度を向上 できる 1. PHPスクリプトを読み込む
2. パースして中間コードに変換 3. 中間コードを実行 4. 中間コードをキャッシュ保存 5. 次回以降、キャッシュした中間コード を再利用 ; OPcacheを有効化 opcache.enable=1 ; CLIスクリプトでも有効化(任意) opcache.enable_cli=1 ; キャッシュサイズを設定(単位: MB) opcache.memory_consumption=128 ; キャッシュするスクリプトの最大数 opcache.max_accelerated_files=10000 ; キャッシュが更新されるタイミング opcache.revalidate_freq=2 PHP実行の流れとopcache opcacheの設定
© DMM.com コンテナサービスとの親和性 31 スピンアップが早い ❏ opcacheで速度改善できるが初回リクエストがくるまでは動的につくるのでスケール前提のコン テナサービスでは上手く作用しづらい ❏ 初回前にキャッシュを作ることもできるが設定が煩雑になったり別途スクリプトが必要に
なったりする ❏ Goは多くの設定をしなくても一定以上のパフォーマンスが出るので、スピンアップという点では優 位があった
© DMM.com まとめ 32 ❏ 開発中は紆余曲折あったけどなんとかリリースまで出来た ❏ ここをコストと思うか投資と思うかで、辛みが違う ❏ PHPからGoへの移行の注意点は、開発初期に集約されているように思える
❏ 開発チームへのGo言語の浸透 ❏ プロダクトアーキテクチャ設計 ❏ 逆に初期を乗り越えればメリットも多い ❏ 負荷試験でもほぼ調整なしで希望しているパフォーマンスがでていた