Upload
sairoutine
View
255
Download
5
Embed Size (px)
Citation preview
Copyright © DeNA Co.,Ltd. All Rights Reserved.
レガシーな Perl システムに DDD( ドメイン駆動設計 ) を取り入れる
株式会社 DeNA Games Osaka 技術部 人西 聖樹 [email protected]
Copyright © DeNA Co.,Ltd. All Rights Reserved.
YAPC !
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Kansai !
Copyright © DeNA Co.,Ltd. All Rights Reserved.
めでたい !
Copyright © DeNA Co.,Ltd. All Rights Reserved.
自己紹介
人西 聖樹(ひとにし まさき)
Twitter: @sairoutine
株式会社 DeNA Games Osaka
Web アプリケーションエンジニア
生まれは京都、育ちは大阪
Copyright © DeNA Co.,Ltd. All Rights Reserved.
関西以外から来られたかたどれくらいいます?
Copyright © DeNA Co.,Ltd. All Rights Reserved.
突然ですが
Copyright © DeNA Co.,Ltd. All Rights Reserved.
本セッションは全て関西弁でお送りします。
Copyright © DeNA Co.,Ltd. All Rights Reserved.
それではやっていきまんねん
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ウソです
Copyright © DeNA Co.,Ltd. All Rights Reserved.
関西人は「〜まんねん」使いません
※ 独自調査
Copyright © DeNA Co.,Ltd. All Rights Reserved.
でも飴のことを「あめちゃん」って言う人は多いです
※ 独自調査
Copyright © DeNA Co.,Ltd. All Rights Reserved.
改めてよろしくお願いします。
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ご当地スポンサー枠
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Mobage
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Mobage 10 周年
Copyright © DeNA Co.,Ltd. All Rights Reserved.
実はもう 11 周年 !
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Copyright © DeNA Co.,Ltd. All Rights Reserved.
まだまだ1000 タイトル以上が稼働中
Copyright © DeNA Co.,Ltd. All Rights Reserved.
DeNA 2016 年度 第 3 四半期決算説明会資料より引用
126億
56 億
Copyright © DeNA Co.,Ltd. All Rights Reserved.
まだまだ日本最大規模のプラットフォームであることに変わりはありません
Copyright © DeNA Co.,Ltd. All Rights Reserved.
そんな Mobage は、Perl で作られています
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Perl 5.8.8
Copyright © DeNA Co.,Ltd. All Rights Reserved.
独自フレームワーク MobaSiF
Copyright © DeNA Co.,Ltd. All Rights Reserved.
今日はなすこと
僕らが開発・運用しているシステムについて
レガシーコード改善の歴史
ドメイン駆動設計の導入
学び
Copyright © DeNA Co.,Ltd. All Rights Reserved.
今日はなすこと
僕らが開発・運用しているシステムについて
レガシーコード改善の歴史
ドメイン駆動設計の導入
学び
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Mobage の 1 ブラウザゲームタイトル
いわゆる「ソーシャルゲーム」
システムとしては、 LAMP(Linux, Apache, MySQL, Perl) + Memcached etc…
Copyright © DeNA Co.,Ltd. All Rights Reserved.
通常の機能に加えて、期間限定のイベントを月に 4 回開催してます。
イベント自体は、前月のシステムを使いまわしていますが、毎月何かしらの機能追加/改修を行ってます。
たまに新規イベントとかも作ったりします。
新規イベントは、開発期間 3 ヶ月〜 6 ヶ月と結構長い
通常のゲームの機能をあまり流用しないことが多く、 1 からゲームを作る感覚。
Copyright © DeNA Co.,Ltd. All Rights Reserved.
アーキテクチャ
Copyright © DeNA Co.,Ltd. All Rights Reserved.
MVC
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Model
情報を扱うクラス O/R マッパー RDBMS の 1 テーブル 1 クラス フレームワーク or ライブラリが提供してない 独自実装 Rails の Active Record に影響受けてそうな API
# 1 行取得my $record = Class->find($pk);# 1 行 UPDATE$record->hp(100);$record->save();
Copyright © DeNA Co.,Ltd. All Rights Reserved.
View
Mobasif 付属のテンプレートエンジン「 Mtemplate 」 ガラケー 3 キャリアに対応する HTML を 1 つのテンプ
レートで記載可能 事前コンパイルにより、リクエストごとのテンプレートの
パースを省略 主要な部分は XS で書かれており、高速
<!-- 条件分岐 -->$ if (condition) { $ <div> hoge </div>$ } elsif (conditions) { $ <div>fuga</div>$ } else { $ <div>piyo</div>$ } $
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Controller
開発者内では、 Page 層と呼称されることが多い
Conf ファイルに、 URL と、 URL に該当するクラス/メソッドを記載する
入力値の validation
Copyright © DeNA Co.,Ltd. All Rights Reserved.
5 年くらい運用してくると出て来る課題
人の入れ替わり
→実装した人が既に退職してる。 5 年も運用してると、現在のチームメンバーが書いたコードは、システムの 10% くらいとか。
知識の継承
知識の継承が出来ておらず、追加実装/変更に対して、いちいち既存の実装の調査に工数がかかる。既存の実装を弄ると、あらぬところにエンバグする。
仕様の肥大化
そもそも実装だけでなくシステムの仕様も肥大化していて、新規仕様を追加するに当たって、既存仕様との不整合による考慮漏れが発生する。
Copyright © DeNA Co.,Ltd. All Rights Reserved.
今日はなすこと
僕らが開発・運用しているシステムについて
レガシーコード改善の歴史
ドメイン駆動設計の導入
学び
Copyright © DeNA Co.,Ltd. All Rights Reserved.
レガシー Perl 特有の辛さ
Copyright © DeNA Co.,Ltd. All Rights Reserved.
EUC-JP
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Shift_JIS
Copyright © DeNA Co.,Ltd. All Rights Reserved.
データベースは Shift_JIS
プログラムは EUC-JP
スマートフォン向け出力は UTF-8
ガラケー向け出力は Shift_JIS
Copyright © DeNA Co.,Ltd. All Rights Reserved.
連想配列リファレンスによる引数の受け渡し
→連想配列の作成元までコードを読まないとどういう挙動の コードかわからない。
# 例sub getEquipmentsString { my ($items) = @_;
if ( scalar(@$items) ) { my @strings = ();
for my $rhItem (@$items) { push(@strings, "$rhItem->{name}x$rhItem->{num}"); }
return join('/', @strings); } else { return undef; }}
Copyright © DeNA Co.,Ltd. All Rights Reserved.
その他、個人的に辛いなぁと思ったこと
全てを管理する神クラス
引数の連想配列リファレンスを変更する addXXXInfo 的なメソッド
超巨大な if 文 (30 回以上 if ~ elsif し続ける )
値が変更されうるグローバル変数 (!) プラットフォーム側と 1枚岩の超モノリ
シックなシステムなところとか
Copyright © DeNA Co.,Ltd. All Rights Reserved.
むむ、ちゃんとオブジェクト指向やれば解決しそう感
Copyright © DeNA Co.,Ltd. All Rights Reserved.
これまでもオブジェクト指向はちょっとずつ取り入れてきました
Copyright © DeNA Co.,Ltd. All Rights Reserved.
DAO 層の追加
Data Access Object RDBMS のデータをオブジェクトとして扱える
Mobasif には DA (DBI クラスのラッパー ) しかなかった
オブジェクトを扱うだけで、 SQL を発行できるので、 SQL 関係のバグが減る
SQL をコントローラーなどに書かずに済む
共通で使われるような SQL を一箇所に集約できる
ゲームロジックについては引き続き Controller 側あった
→ Fat Controller になる問題
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Model 層の追加
RDBMS との連携だけでなく、状態を操作する処理も担当する
get_group_info メソッドとか increment_user_num メソッドとか
そのうち、他の Model を操作するコードとかも増える
長大なゲームロジックのコードも増えだす
今度は Fat Model になってくる問題が
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Logic 層の追加
Model からゲームロジックに関する部分を切り出す
複数の Model の操作や、処理順序通りの実行などを行う
Copyright © DeNA Co.,Ltd. All Rights Reserved.
残る課題感
インスタンス化してるけど結局 Func っぽい使い方のクラス
→ クラスメソッドで良いのでは
→ むしろ状態を持つと、状態の遷移を考慮しないといけなくなる
せっかくオブジェクト指向で書いてるのに if文がたくさん!
→ ポリモーフィズムとは
new に連想配列リファレンスを渡して、外から編集したり参照したり
→ カプセル化したい
Copyright © DeNA Co.,Ltd. All Rights Reserved.
もうちょっとオブジェクト指向における指針が欲しい
Copyright © DeNA Co.,Ltd. All Rights Reserved.
特にビジネスロジックに焦点を当てた ...
Copyright © DeNA Co.,Ltd. All Rights Reserved.
今日はなすこと
僕らが開発・運用しているシステムについて
レガシーコード改善の歴史
ドメイン駆動設計の導入
学び
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ドメイン駆動設計とは
厳しい現実の中で、ソフトウェア設計を習得しようと奮闘してきた技術者の物語。不完全な状況の中で、抽象的な設計原則を、現実のソフトウェアに適用するための助言。
日本語版への序文 by エリック・エヴァンス
Copyright © DeNA Co.,Ltd. All Rights Reserved.
バイブル
Copyright © DeNA Co.,Ltd. All Rights Reserved.
より実践的な方
Copyright © DeNA Co.,Ltd. All Rights Reserved.
哲学と戦術
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ドメイン駆動設計とは哲学ドメインモデルユビキタス言語戦術エンティティ値オブジェクトサービスレイヤー化アーキテクチャ
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ドメインモデルとレイヤ化アーキテクチャにフォーカスする
Copyright © DeNA Co.,Ltd. All Rights Reserved.
レイヤ化アーキテクチャ
『レイヤ化アーキテクチャ』による『ドメイン層の隔離』
ドメイン層を他階層と分ける事により、各層の責務を明確化する。
特にドメイン層が隔離されることで、後のビジネスロジックの進化に実装がついていきやすくなる。
ドメイン層にミドルウェアのためのコードやユーザーの入力のためのコードを入れない
Copyright © DeNA Co.,Ltd. All Rights Reserved.
レイヤー化アーキテクチャ
ユーザインターフェース層 ユーザに情報を表示し、ユーザの入力を認識
→ View, Controller アプリケーション層 ドメイン層のオブジェクトを協調させ、作業
の調整を行う
→ Controller, Service ドメイン層 ロジックの表現
→ Domain インフラ層 上位レイヤを支える
→ DAO 。 MySQL, Memcached のスキーマと、ドメインモデルの差異を吸収する。
Copyright © DeNA Co.,Ltd. All Rights Reserved.
やったこと
Domain と Service 層の追加
Domain はドメインモデルを表す
オブジェクト指向におけるオブジェクト (語弊を怖れず言うと )
世界を抽象化した際の物体
Service はドメイン同士を組み合わせた処理
及びそのワークフローを組み立てる
Service は状態を持たない
→既存の Logic 層を Domain と Service に分解していく
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ロジックをDomain と Service に分解していく
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ボスへの攻撃の処理を考えてみる
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ドメインモデルの抽出
「『ユーザー』が『ボス』に攻撃する」 →Domain::User, Domain::Boss の発見 「『アイテム』を使って攻撃する」 Domain::UserItem の発見 「バトルに参加して攻撃する」 →Domain::BattleField の発見
→5W1H でドメインモデルを発見していく
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ドメインモデルの発見
ユーザーとは?→ デッキを持つ→ デッキは 6枚のカードで構成される→ Domain::Deck の発見、 Domain::Card の発見
ボスとは?→期間限定ボス、シングルボス、マルチボスなど種類が存在
する→ Domain::Boss:Scheduled, Domain::Boss::Single,
Domain::Boss:Multi の発見
→ Domain をさらに分解していくと、さらに Domain を発見できる
Copyright © DeNA Co.,Ltd. All Rights Reserved.
「攻撃する」処理
Service::Attack→ 各種ドメインを組み合わせて、「攻撃」時に起こる各ドメ
インの状態を操作する→ユーザーのデッキの HP, ボスの HP, 消費リソースの減少
etc…
サービスは状態を持たない→あくまで状態はドメインが持つ
Service::Attack->exec($user, $boss, $deck, $item, $battlefield etc..);
→サービスのメソッドは引数が長くなりがち…
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Service
Service は処理の順番を担保する 攻撃ならば → アイテムの所持チェック → アイテムの消費 → デッキの攻撃力計算 → ボスの攻撃力計算 → デッキの HP減少 → ボスの HP減少 → ユーザーの行動ログの保存etc..
Copyright © DeNA Co.,Ltd. All Rights Reserved.
機能追加の設計をする際にエンジニアが考えること
1. ドメインモデルの発見 →機能について関わる物体 (オブジェクト ) を洗
う。 →洗ったオブジェクトが持つと考えられるメソッ
ドを洗う →Domain::User なら reduce_item,
Domain::Card なら calc_attack 等
2. ドメイン同士の処理の順番を考える ( ワークフロー )
→アイテム消費→ボスの HP減少→味方の HP減少 etc..
Copyright © DeNA Co.,Ltd. All Rights Reserved.
議論になったところ (抜粋 )
Copyright © DeNA Co.,Ltd. All Rights Reserved.
古いコードとの接続をどうするか?
ゲーム共通で使用するアイテム等。
リファクタできる箇所はリファクタした。
できなければラッパークラスを作って隠蔽した(デザパタで言う Adaper )
Copyright © DeNA Co.,Ltd. All Rights Reserved.
Service は状態を持つべきか否か
→今後の機能改修で、 Service が複雑な状態操作をするようになっていくことを怖れて、状態を持たせない
my $service = Service->new(%args); # 必要な User や Boss を渡す$service->exec(); # 実行
Copyright © DeNA Co.,Ltd. All Rights Reserved.
チームメンバーへの理解 新規の機能追加にアサインされたので、まず僕がコードを書
くところから 事前の根回し (オブジェクト勉強会/別プロジェクトの勉強
会等の情報 ) 実装は、ドメイン駆動/実践ドメイン駆動読みつつサグりサグり
もう一人のアサインされたエンジニアには、合意だけ取って まず僕がコード書いて、相方にはコードベースで設計を共有 今思えばあまりよくなかった (口頭/レビューで設計をすり
あわせるべき ) 導入後は、社内勉強会で他のメンバーへ擦り合わせ 僕/相方が関わるプロジェクトでコードがどんどん増えて
いった コードを書く前の勉強会はピンとこない
Copyright © DeNA Co.,Ltd. All Rights Reserved.
今日はなすこと
僕らが開発・運用しているシステムについて
レガシーコード改善の歴史
ドメイン駆動設計の導入
学び
Copyright © DeNA Co.,Ltd. All Rights Reserved.
以下が解決
インスタンス化してるけど結局 Func っぽい使い方のクラス
→ ドメインモデルとして捉えられるものがインスタンス化できるクラス
→ むしろ状態を持つと、状態の遷移を考慮しないといけなくなる
せっかくオブジェクト指向で書いてるのに if文がたくさん!
→ ポリモーフィズムで if を排除 ( これはただのオブジェクト指向 )
new に連想配列リファレンスを渡して、外から編集したり参照したり
→ ドメインモデルをコードに落とし込み、メソッドとプロパティを定義することで、クラスの API が明確に。
Copyright © DeNA Co.,Ltd. All Rights Reserved.
学び
あれ?これは結局ただのオーソドックスなオブジェクト指向じゃない?感
→戦術だけ見てドメイン駆動設計をやり始めると、そうなる→大切なことは哲学の方
ドメイン駆動設計の解釈は人それぞれ。チーム内で認識をすりあわせることが大切
→理論が正ではなく、チームで共有できた認識が最も正しい
Copyright © DeNA Co.,Ltd. All Rights Reserved.
未来
新規機能でドメイン駆動設計を実践する知見は溜まったので、既存のコードも置き換えていっている
実践したことで、設計についてチームで共通認識があるのは良い
要件定義/仕様作成の段階で、ユビキタス言語も取り入れていきたい
Copyright © DeNA Co.,Ltd. All Rights Reserved.
ご清聴ありがとうございました