株式会社永産システム開発 / backendエンジニア
大手塾 eラーニングシステムの開発
# プロジェクト概要 ## 目的・背景 某大手塾が提供するeラーニングシステムの設計・実装を担当した。 このシステムは公立中学・高校をターゲットに、web学習、成績集計、AIによる個別最適化学習、課題配信など、生徒・教師の学習フローの最適化を目的としている。 特に、cmi5という米国国防省内の標準化機関が2016年に発表した規格に準拠しており、AI学習もサポートしている。 ## 規模感、チーム構成、担当した役割 本プロジェクトは複数の会社が参画し、弊社はbackendの実装を担当した。 また、backendと各サービス間の結合部分の詳細設計も弊社側で取り組んだ。 全体の統括や大枠の設計は別の会社が行っていた。 以下が本アプリケーションを構成するサービスである。 backendは主に、frontendのリクエストを起点に、LRSやAIと連携しながらデータの生成やレスポンス生成等を行っている。 * frontend * backend <- 弊社担当 * LRS(データストア) * AI ## 使用技術 言語: Ruby、 JavaScript フレームワーク: Ruby on Rails DB: MySQL インフラ: Docker、 AWS その他: OpenAPI Spec # 開発・実装内容 ## 1. frontend向けAPIの作成 ### 概要 本backendサービスは、別サーバーでホスティングされているfrontendサービスのリクエストに対するレスポンスを、WebAPIを通して行う必要があった。 ### 課題・問題点 開発している会社が異なることによるコミュニケーションコストや、仕様のすり合わせ等のオーバーヘッドが考えられた。 ### 打ち手・使用した技術 OpenAPI Specを使用した標準的な仕様書の導入を行い、多部隊感でのコミュニケーションや結合に関するコストの低減を行った。 ### 成果 OpenAPI Specを使用した事で、仕様書がコードベースになり、gitによる管理ができるようになった。 これにより、改修や結合等に関わるコミュニケーションコストを削減できた。 ## 2. AIサーバーとのデータ連携 ### 概要 backendとAIはそれぞれ個別にDBを持っており、同じデータを保持する必要があるという仕様であった。 また、backendからAI側へのデータ書き込みはWebAPIを使用して行う設計であった。 このデータ書き込みを行うAPIはAI側で用意されているものであり、各テーブルごとに個別のAPIが割り当てられていた。 これらのAPIは都度改修や、今後もAPIの追加等が考えられた。 ### 課題・問題点 上記仕様を守るために、backendとAIでデータ齟齬を発生させないようにする必要があった。 つまり、backend側にデータが作成されるのであれば、AI側にも同じデータを生成する必要があり、逆に、どちらかのデータ作成が失敗した場合には両方でデータ作成を行わない仕組みが必要であった。 また、メンテナンス性や、拡張性を考慮した設計を行う必要もあった。 ### 打ち手・使用した技術 上記のようなデータの整合性を保証するために一般的に使用される技術としてはトランザクションがあり、Railsはトランザクションを標準的にサポートしている。 しかし、Railsが標準的にサポートしているトランザクションは、Railsアプリケーション内で完結するデータ書き込みである。 そこで、modelコールバックを使用したトランザクションを設計することで対応した。 Rails側にデータ作成・更新を行い、コミットするまでの間にAI側へのデータ書き込みのリクエストを行い、AI側が何らかの理由により書き込みに失敗した際にはRails側でも明示的にエラーを挙げ、コミットをキャンセルすることでトランザクションを実現した。 また、メンテナンス性、拡張性を高めるために、AI側のAPIと疎通するためのクラスはGoFのデザインパターンにおける、Template Methodパターンを使用した設計を採用した。 さらに、backendでは1つのレコードであるが、AI側では複数のレコードに分割するような書き込みを行う際にはRubyのマルチスレッド機能を使用した実装にすることで、書き込み速度の向上も行った。 ### 成果 当初の設計では、backend・AI間におけるデータ齟齬由来のバグの発生まで考慮されていなかった。 私のこの設計・実装により、2つのサービス間で齟齬のないデータ保持が可能になり、安定的なアプリケーション運用を実現できた。 ## 3. 成績集計に関する定期実行バッチ処理 ### 概要 本アプリケーションでは成績を集計して表示・分析するための機能が存在する。 この機能では、集計対象のパラメータとして、生徒、教科、学習パターン、期間等が選択可能であった。 また、集計は各生徒の1問ごとに生成される解答データをもとに行う必要があった。 ### 課題・問題点 当初の設計ではエンドユーザーの成績集計リクエストのたびに、対象の集計を行いレスポンスする設計であった。 しかしエンドユーザーが任意の集計パラメータでリクエストした場合に、対象データ数が膨大なことにより、レスポンスが遅くなることが考えられた。 そこで、レスポンス速度を向上させること、また、コンピューターリソースの圧迫を抑えることを実現する工夫が必要であった。 ### 打ち手・使用した技術 上記問題を改善するために以下の技術を使用した。 * cronを使用した定期実行処理(夜間)の実装 * sidekiqを使用した非同期処理の実装 これらの技術を使用し、夜間の定期実行非同期バッチ処理により、各生徒の日毎の成績の集計を行い、成績集計リクエスト時の計算量を減らすためのデータを作成するように実装した。 また、成績集計リクエストはクラス単位で行われることが多いため、別途クラス単位での集計データも生成するようにした。 ### 成果 当初の設計に対し、私が上記で実装した内容により、成績集計の計算速度を向上させられた。 本アプリケーションでは、レスポンス速度の向上はプライオリティの高い非機能要件であったようで、統括会社様から、この工夫を評価いただけた。 # 本プロジェクトを通して学んだこと * 別サーバーで稼働するサービスとのWebAPIを使用したやり取り。OpenAPI specによる仕様書の共有。データ齟齬を発生させないためのトランザクション等。 * cronを使用した定期実行処理 * sidekiqを使用した非同期 * ドメイン駆動設計を採用した、modelからビジネスロジックを分離させる実装。 * WebAPIに於いて、ActiveModel::Serializerを用いたJSONレスポンスの生成。 * デザインパターンを踏襲した再利用性・拡張性の高いクラス設