Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れる

1,057 views

Published on

YAPC::Kansai 2017
http://yapcjapan.org/2017kansai/

Published in: Engineering
  • Be the first to comment

レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れる

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

×