DeNAのゲーム基盤LCXでのインターン体験記

by Kenta Tanigaki | June 15, 2021
intern | #java #quarkus #web-application #software-architecture

はじめに

ソリューション事業本部エンタープライズサービスグループでインターンをしている谷垣です。 普段は他社でWeb開発のアルバイトをしたり、大学院で人工知能の研究をしたりしています。 今回参加したインターンではLCXというプロジェクトに参加し、主にバックエンドの開発を行いました。 この記事ではインターンの参加レポートと、参加して何を得たかについて、技術的な内容を交えながら述べていこうと思います。 DeNAのインターンに興味がある方の参考の一助になればと思います。

LCXについて

LCX は、ゲームの汎用的な機能である「アプリ内課金」や「リモート通知」などを利用するための抽象的なインターフェースを提供する社内向けサービスです。 DeNAではスマートフォン向けのゲームを開発しているため、一部のタイトルでバックエンドとして利用されています。 LCXは大きく分けて以下の2つで構成されており、今回はLCX Serverの開発に参加させていただきました。 私はゲームに関わるバックエンドに興味があり、大規模なプロジェクトに携わりたいと思っていたため、今回のインターンは希望通りとなりました。

  • LCX SDK: ゲーム側からLCXを利用するためのSDK
  • LCX Server: LCXの本体となるサーバー

インターンの流れ

プロジェクトの把握と環境構築

最初に行ったことは、LCXのプロジェクトについて把握することでした。 しかしLCXの仕様は膨大であり、全てを把握するには今回のインターン期間では短すぎました。 インターンの主な目的は開発現場を体験することだったため、開発を進める上で最低限の情報を得て、とにかく実装に移ることを優先することになりました。 このように、個人の目的に応じてインターンを進めて頂けることは有り難いと思いました。

次に環境構築ですが、LCX Serverは主にJavaとQuarkusで開発されており、私は既にJavaとSpringを用いた開発を経験したことがあったため、幸いにもスムーズに環境を構築することができました。 これは、LCXの開発環境が非常に整備されており、煩雑な手順が無かったことも大きかったです。 このように、自分たちの開発体験を良くしようとする取り組みが活発に行われていることも、参加してみて感じた良い点の一つでした。

シンプルなAPIの実装

最初に取り組んだ開発タスクは、LCX ServerにシンプルなAPIを実装することでした。 主な目的は、JavaとQuarkusによる開発に慣れると共に、LCX Serverのアーキテクチャを把握することでした。 LCXほどの大規模なコードベースに変更を加えることは初めての経験であり、コードリーディングやソフトウェアアーキテクチャなどの技術も得られたと思います。

分からない点がある時は、プロジェクトメンバーの方にSlackやZoomなどで気軽に質問することができる点も良いと思いました。 そのため、リモートワークながらも意思疎通で困った点は特にありませんでした。

外部連携に依存した問題への対処

次に取り組んだ開発タスクは、購入処理における外部連携に依存した問題に対処するという、より実践的な内容でした。 LCXにはアプリ内課金の機能があり、App StoreやGoogle Playといったモバイルアプリのストアと連携して購入処理を行っています。 その中で、App Storeとの連携部分で発生していた問題がありました。 購入処理の中にはApp Storeで課金を行ったユーザのレシートを検証する部分があり、そのためにApp Storeにレシートを送信する必要があります。 そして検証結果として返されるresponseBody.Receipt.In_appという配列の中のデータが購入処理に必要となります。 しかしApp Store側の問題により、この配列が空になってしまうケースがありました。 この問題自体の原因は不明ですが、LCX側でこのケースに対処する必要があり、今回はこのタスクを担当しました。

今回の対応として、In_appが空の場合、その注文の購入処理をスキップできるなら何もせずにOKを返却し、スキップできないならエラーを返す、という方法をとりました。 これにより、In_appが空の場合の処理をLCX側でコントロールできるようになります。 そして、それぞれの注文の購入処理をスキップできるかどうかはデータベースで管理するようにしました。

ただ、実装上で考慮すべき点がありました。 以下の図に例外処理の流れを示します。 最初はこの流れで実装を行い、正常に動作すると思っていました。 しかしメンターの方に指摘を頂き、Purchase UsecaseからRepositoryを経由してデータベースに注文情報の書き込みを行う部分(赤字)が、正常に行われないことに気付きました。 これは、Purchase Usecaseがトランザクション境界となっているため、ここからRuntime Exceptionを行うと、この書き込み処理もロールバック対象となってしまうためです。

例外処理の流れ (修正前)

例外処理の流れ (修正前)

そこでメンターの方に助言を頂きながら、最終的には以下の図のような方法をとることにしました。 まずUsecaseを、購入処理を行うPurchase Usecaseと、空のIn_appのエラーハンドリングを行うError Handle Usecaseに分割しました。 そしてPurchase Usecaseだけにトランザクションを張り、データベースへの書き込み処理はError Handle Usecaseで行いました。 これにより、In_appが空の場合は購入処理だけロールバックされ、かつ注文情報はデータベースに書き込まれ、エラーレスポンスが返却されるようになります。 これでようやく、理想的な例外処理を実現することができました。 当初考えていたよりも修正量が多くなってしまいましたが、実装力が身に付いたと思います。

例外処理の流れ (修正後)

例外処理の流れ (修正後)

おわりに

今回のインターンは約1ヶ月半と短期間での参加となりましたが、自分にとって非常に実りのあるものとなりました。 目的だった開発現場を体験することもできましたし、技術力を身に付けながらプロダクトに貢献できて良かったです。 総じて、就活的にも技術的にも良い影響を受けることができたインターンでした。 メンターをしてくださった橋本さんや、國枝さんをはじめとする開発メンバーの方には技術面で手厚いサポートをしていただき、 斎藤さんや21卒・22卒の方にはキャリア面で相談に乗っていただき、本当にお世話になりました。 最後に、インターン期間中に関わって下さった皆様に感謝致します。