More Related Content
Similar to レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れる (20)
レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れる
- 1. Copyright © DeNA Co.,Ltd. All Rights Reserved.
レガシーな Perl システムに
DDD(ドメイン駆動設計) を
取り入れる
株式会社 DeNA Games Osaka
技術部 人西 聖樹
masaki.hitonishi@dena.com
- 5. Copyright © DeNA Co.,Ltd. All Rights Reserved.
自己紹介
人西 聖樹(ひとにし まさき)
Twitter: @sairoutine
株式会社 DeNA Games Osaka
Webアプリケーションエンジニア
生まれは京都、育ちは大阪
- 12. Copyright © DeNA Co.,Ltd. All Rights Reserved.
でも飴のことを
「あめちゃん」って
言う人は多いです
※独自調査
- 22. Copyright © DeNA Co.,Ltd. All Rights Reserved.
DeNA 2016年度 第3四半期決算説明会資料より引用
126億
56億
- 23. Copyright © DeNA Co.,Ltd. All Rights Reserved.
まだまだ日本最大規模の
プラットフォームで
あることに変わりはありません
- 27. Copyright © DeNA Co.,Ltd. All Rights Reserved.
今日はなすこと
僕らが開発・運用しているシステムについて
レガシーコード改善の歴史
ドメイン駆動設計の導入
学び
- 28. Copyright © DeNA Co.,Ltd. All Rights Reserved.
今日はなすこと
僕らが開発・運用しているシステムについて
レガシーコード改善の歴史
ドメイン駆動設計の導入
学び
- 29. Copyright © DeNA Co.,Ltd. All Rights Reserved.
Mobage の 1 ブラウザゲームタイトル
いわゆる「ソーシャルゲーム」
システムとしては、LAMP(Linux, Apache,
MySQL, Perl) + Memcached etc…
- 30. Copyright © DeNA Co.,Ltd. All Rights Reserved.
通常の機能に加えて、期間限定のイベントを月に4回開催し
てます。
イベント自体は、前月のシステムを使いまわしていますが、
毎月何かしらの機能追加/改修を行ってます。
たまに新規イベントとかも作ったりします。
新規イベントは、開発期間3ヶ月〜6ヶ月と結構長い
通常のゲームの機能をあまり流用しないことが多く、1から
ゲームを作る感覚。
- 33. 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();
- 34. 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>
$ } $
- 35. Copyright © DeNA Co.,Ltd. All Rights Reserved.
Controller
開発者内では、Page 層と呼称されることが
多い
Conf ファイルに、URL と、URLに該当する
クラス/メソッドを記載する
入力値のvalidation
- 36. Copyright © DeNA Co.,Ltd. All Rights Reserved.
5年くらい運用してくると出て来る課題
人の入れ替わり
→実装した人が既に退職してる。5年も運用してると、現在のチームメン
バーが書いたコードは、システムの10%くらいとか。
知識の継承
知識の継承が出来ておらず、追加実装/変更に対して、いちいち既存の実
装の調査に工数がかかる。既存の実装を弄ると、あらぬところにエンバ
グする。
仕様の肥大化
そもそも実装だけでなくシステムの仕様も肥大化していて、新規仕様を追
加するに当たって、既存仕様との不整合による考慮漏れが発生する。
- 37. Copyright © DeNA Co.,Ltd. All Rights Reserved.
今日はなすこと
僕らが開発・運用しているシステムについて
レガシーコード改善の歴史
ドメイン駆動設計の導入
学び
- 41. Copyright © DeNA Co.,Ltd. All Rights Reserved.
データベースは Shift_JIS
プログラムは EUC-JP
スマートフォン向け出力は UTF-8
ガラケー向け出力は Shift_JIS
- 42. 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;
}
}
- 43. Copyright © DeNA Co.,Ltd. All Rights Reserved.
その他、個人的に辛いなぁと思ったこと
全てを管理する神クラス
引数の連想配列リファレンスを変更する
addXXXInfo 的なメソッド
超巨大な if 文(30回以上 if ~ elsif し続ける)
値が変更されうるグローバル変数 (!)
プラットフォーム側と1枚岩の超モノリシッ
クなシステムなところとか
- 45. Copyright © DeNA Co.,Ltd. All Rights Reserved.
これまでもオブジェクト指向は
ちょっとずつ取り入れてきました
- 46. Copyright © DeNA Co.,Ltd. All Rights Reserved.
DAO 層の追加
Data Access Object
RDBMSのデータをオブジェクトとして扱える
Mobasif には DA (DBI クラスのラッパー)しかなかった
オブジェクトを扱うだけで、SQL を発行できるので、SQL
関係のバグが減る
SQL をコントローラーなどに書かずに済む
共通で使われるような SQL を一箇所に集約できる
ゲームロジックについては引き続き Controller 側あった
→ Fat Controller になる問題
- 47. Copyright © DeNA Co.,Ltd. All Rights Reserved.
Model 層の追加
RDBMS との連携だけでなく、状態を操作する処理
も担当する
get_group_info メソッドとか
increment_user_num メソッドとか
そのうち、他の Model を操作するコードとかも増
える
長大なゲームロジックのコードも増えだす
今度は Fat Model になってくる問題が
- 48. Copyright © DeNA Co.,Ltd. All Rights Reserved.
Logic 層の追加
Model からゲームロジックに関する部分を
切り出す
複数の Model の操作や、処理順序通りの実
行などを行う
- 49. Copyright © DeNA Co.,Ltd. All Rights Reserved.
残る課題感
インスタンス化してるけど結局 Funcっぽい使い方のクラス
→ クラスメソッドで良いのでは
→ むしろ状態を持つと、状態の遷移を考慮しないといけなくなる
せっかくオブジェクト指向で書いてるのにif文がたくさん!
→ ポリモーフィズムとは
newに連想配列リファレンスを渡して、外から編集したり参照したり
→ カプセル化したい
- 52. Copyright © DeNA Co.,Ltd. All Rights Reserved.
今日はなすこと
僕らが開発・運用しているシステムについて
レガシーコード改善の歴史
ドメイン駆動設計の導入
学び
- 53. Copyright © DeNA Co.,Ltd. All Rights Reserved.
ドメイン駆動設計とは
厳しい現実の中で、
ソフトウェア設計を習得しようと
奮闘してきた技術者の物語。
不完全な状況の中で、
抽象的な設計原則を、
現実のソフトウェアに
適用するための助言。
日本語版への序文 by エリック・エヴァンス
- 57. Copyright © DeNA Co.,Ltd. All Rights Reserved.
ドメイン駆動設計とは
哲学
ドメインモデル
ユビキタス言語
戦術
エンティティ
値オブジェクト
サービス
レイヤー化アーキテクチャ
- 58. Copyright © DeNA Co.,Ltd. All Rights Reserved.
ドメインモデルと
レイヤ化アーキテクチャに
フォーカスする
- 59. Copyright © DeNA Co.,Ltd. All Rights Reserved.
レイヤ化アーキテクチャ
『レイヤ化アーキテクチャ』による『ドメイン層
の隔離』
ドメイン層を他階層と分ける事により、各層の責
務を明確化する。
特にドメイン層が隔離されることで、後のビジネ
スロジックの進化に実装がついていきやすくなる。
ドメイン層にミドルウェアのためのコードやユー
ザーの入力のためのコードを入れない
- 60. Copyright © DeNA Co.,Ltd. All Rights Reserved.
レイヤー化アーキテクチャ
ユーザインターフェース層 ユーザに情報を表示し、ユーザの入力を認
識
→ View, Controller
アプリケーション層 ドメイン層のオブジェクトを協調させ、作業の調
整を行う
→ Controller, Service
ドメイン層 ロジックの表現
→ Domain
インフラ層 上位レイヤを支える
→ DAO。MySQL, Memcached のスキーマと、ドメインモデルの差異を吸
収する。
- 61. Copyright © DeNA Co.,Ltd. All Rights Reserved.
やったこと
Domain と Service 層の追加
Domain はドメインモデルを表す
オブジェクト指向におけるオブジェクト(語弊を怖れず言うと)
世界を抽象化した際の物体
Service はドメイン同士を組み合わせた処理
及びそのワークフローを組み立てる
Service は状態を持たない
→既存の Logic 層を Domain と Service に分解していく
- 62. Copyright © DeNA Co.,Ltd. All Rights Reserved.
ロジックを
Domain と Service に分解していく
- 64. Copyright © DeNA Co.,Ltd. All Rights Reserved.
ドメインモデルの抽出
「『ユーザー』が『ボス』に攻撃する」
→Domain::User, Domain::Boss の発見
「『アイテム』を使って攻撃する」
Domain::UserItem の発見
「バトルに参加して攻撃する」
→Domain::BattleField の発見
→5W1H でドメインモデルを発見していく
- 65. Copyright © DeNA Co.,Ltd. All Rights Reserved.
ドメインモデルの発見
ユーザーとは?
→ デッキを持つ
→ デッキは6枚のカードで構成される
→ Domain::Deck の発見、Domain::Card の発見
ボスとは?
→期間限定ボス、シングルボス、マルチボスなど種類が存在す
る
→ Domain::Boss:Scheduled, Domain::Boss::Single,
Domain::Boss:Multi の発見
→ Domain をさらに分解していくと、さらに Domain を発見
できる
- 66. Copyright © DeNA Co.,Ltd. All Rights Reserved.
「攻撃する」処理
Service::Attack
→ 各種ドメインを組み合わせて、「攻撃」時に起こる各ドメ
インの状態を操作する
→ユーザーのデッキのHP, ボスのHP, 消費リソースの減少
etc…
サービスは状態を持たない
→あくまで状態はドメインが持つ
Service::Attack->exec($user, $boss, $deck, $item,
$battlefield etc..);
→サービスのメソッドは引数が長くなりがち…
- 67. Copyright © DeNA Co.,Ltd. All Rights Reserved.
Service
Service は処理の順番を担保する
攻撃ならば
→ アイテムの所持チェック
→ アイテムの消費
→ デッキの攻撃力計算
→ ボスの攻撃力計算
→ デッキのHP減少
→ ボスのHP減少
→ ユーザーの行動ログの保存
etc..
- 68. Copyright © DeNA Co.,Ltd. All Rights Reserved.
機能追加の設計をする際にエンジニアが考えること
1. ドメインモデルの発見
→機能について関わる物体(オブジェクト)を洗う。
→洗ったオブジェクトが持つと考えられるメソッ
ドを洗う
→Domain::Userなら reduce_item,
Domain::Card なら calc_attack 等
2. ドメイン同士の処理の順番を考える(ワークフ
ロー)
→アイテム消費→ボスのHP減少→味方のHP減少
etc..
- 70. Copyright © DeNA Co.,Ltd. All Rights Reserved.
古いコードとの接続をどうするか?
ゲーム共通で使用するアイテム等。
リファクタできる箇所はリファクタした。
できなければラッパークラスを作って隠蔽した(デ
ザパタで言うAdaper )
- 71. Copyright © DeNA Co.,Ltd. All Rights Reserved.
Service は状態を持つべきか否か
→今後の機能改修で、Service が複雑な状態操作をするようになってい
くことを怖れて、状態を持たせない
my $service = Service->new(%args); # 必要な User や Boss を渡す
$service->exec(); # 実行
- 72. Copyright © DeNA Co.,Ltd. All Rights Reserved.
チームメンバーへの理解
新規の機能追加にアサインされたので、まず僕がコードを書
くところから
事前の根回し(オブジェクト勉強会/別プロジェクトの勉強会
等の情報)
実装は、ドメイン駆動/実践ドメイン駆動読みつつサグりサ
グり
もう一人のアサインされたエンジニアには、合意だけ取って
まず僕がコード書いて、相方にはコードベースで設計を共有
今思えばあまりよくなかった(口頭/レビューで設計をすりあ
わせるべき)
導入後は、社内勉強会で他のメンバーへ擦り合わせ
僕/相方が関わるプロジェクトでコードがどんどん増えて
いった
コードを書く前の勉強会はピンとこない
- 73. Copyright © DeNA Co.,Ltd. All Rights Reserved.
今日はなすこと
僕らが開発・運用しているシステムについて
レガシーコード改善の歴史
ドメイン駆動設計の導入
学び
- 74. Copyright © DeNA Co.,Ltd. All Rights Reserved.
以下が解決
インスタンス化してるけど結局 Funcっぽい使い方のクラス
→ ドメインモデルとして捉えられるものがインスタンス化できるクラス
→ むしろ状態を持つと、状態の遷移を考慮しないといけなくなる
せっかくオブジェクト指向で書いてるのにif文がたくさん!
→ ポリモーフィズムで if を排除 (これはただのオブジェクト指向)
newに連想配列リファレンスを渡して、外から編集したり参照したり
→ ドメインモデルをコードに落とし込み、メソッドとプロパティを定義す
ることで、クラスのAPIが明確に。
- 75. Copyright © DeNA Co.,Ltd. All Rights Reserved.
学び
あれ?これは結局ただのオーソドックスなオブジェクト指
向じゃない?感
→戦術だけ見てドメイン駆動設計をやり始めると、そうなる
→大切なことは哲学の方
ドメイン駆動設計の解釈は人それぞれ。チーム内で認識を
すりあわせることが大切
→理論が正ではなく、チームで共有できた認識が最も正しい
- 76. Copyright © DeNA Co.,Ltd. All Rights Reserved.
未来
新規機能でドメイン駆動設計を実践する知見は溜
まったので、既存のコードも置き換えていってい
る
実践したことで、設計についてチームで共通認識
があるのは良い
要件定義/仕様作成の段階で、ユビキタス言語も
取り入れていきたい