
はじめに
みなさん、こんにちは。オンラインサロン開発部 開発グループ アーキテクトチームの谷川です。
DMMオンラインサロンでは現行のシステムにおける課題を解決し、ビジネス的な課題を抽出しつつリプレース・リアーキテクトを進めるプロジェクト「neon」を中期的に取り組んでいます。
今回は私達DMMオンラインサロンがシステムをリプレースする上で行っている、現行データベースから継続的にマイグレーションする際の課題と対策についてご紹介します。
リプレースプロジェクトについて
現行システムについて
現在稼働している現行システムは、大きく分けて2つの主要な部分から構成されています。
1つはユーザーの入退会や管理機能を提供する「入会・オーナー管理画面」で、もう1つはオンラインサロンの活動をサポートする「DMMオンラインサロン専用コミュニティ」(以後「専用コミュニティ」と記載)です。
これらは、それぞれ独立したデータベースを有しています。
現行システムはAWS上で稼働し、データベースはAurora MySQLを利用しています。
現行システムの認可制御は、ロールベースのアクセス制御(RBAC)で行い、認可制御に必要なデータは各データベースで管理しています。
新基盤について
新基盤は、マイクロサービスパターンとモジュラモノリスパターンを組み合わせて開発を進めており、各サービスとモジュラモノリスはそれぞれ独自のデータベースを有しています。
現行システム同様に、新基盤もAWS上で稼働し、データベースはAurora MySQLを利用しています。
新基盤の認可制御は、GoogleのZanzibarという関係性ベースのアクセス制御(ReBAC)を行う認可システムにインスパイアされて開発されたOSSのSpiceDBを採用しています。
認可制御のデータはSpiceDBのAPIを介して管理します。
新基盤の技術スタックの詳細は以前に公開した記事で紹介していますので、理解を深めたい方はぜひご一読ください。
移行方法について
現在、ストラングラーフィグパターンに基づき、段階的に現行システムから新基盤へデータ移行を進めています。
現行システムと新基盤のデータ同期について
現行システムから新基盤への同期
現行システムから新基盤へのデータ同期には、2種類のアプローチを採用しています
ー イベントベースの同期
イベントベースでは、CDC(Change Data Capture)により現行システムのデータベースの変更を検知し、Amazon MSKを通じてAWS Lambdaが受け取り、ニアリアルタイムで新基盤のデータベースに同期を行っています。
ー バッチベースの同期
AWS Fargateが起動するタスクを定期的に実行し、現行システムのデータを新基盤に一括で同期しています。
新基盤への一番最初の一括同期やイベントベースの同期失敗時の再同期に使われます。
現行システムから新基盤へのデータ同期プロセスについても、以前に公開した記事で紹介しています。
新基盤から現行システムへの同期
機能を絞った初期リリースの段階では、大半の機能は現行システム側で稼働します。
このため、新基盤で行われたデータ操作は、継続的に現行システム側に同期させる必要があります。
新基盤から現行システムへの同期は、新基盤側の同期専用サービスを介して行います。
本記事では新基盤側のサービスについては踏み込みませんが、詳細については今後別の記事でご紹介したいと思います。
複雑性の課題
現行システムは、サービスの拡大に伴い、システムを都度拡充していった結果、歪なモノリシックアプリケーション構造に発展しています。

例えば、アカウント情報の一部のデータは「入会・オーナー管理画面」と「コミュニティサービス」のそれぞれのデータベースで重複して扱っているため、どちらかのシステムで起こった変更を、もう一方にも反映する同期処理を行っています。

この構造によるデータフローは複雑で、新基盤を初期リリースした後も続きます。
新基盤はゆがみを解消するために最適化した構造で再設計しています。
当然、新基盤は、現行システムとは大幅に異なる構造になり、現行システムと新基盤とのデータ連携も複雑になります。
つまり、複雑な現行システム間のデータ連携に、構造が大幅に異なる新基盤との双方向連携のパターンが増え、その複雑性はいっそう増します。
例として、新基盤でサロンを作成した際のデータ同期の流れを説明します。
- オーナーが新基盤でサロンを作成する
- 新基盤のデータベースにサロンが新規作成される
- 新基盤から直接現行システムの「入会・オーナー管理画面」のデータベースに接続して新基盤のサロン作成情報を同期する
- 「入会・オーナー管理画面」のサロン作成のINSERTを検知してLambdaに連携
- 現行システムから新基盤へサロン作成情報の同期処理が動くが、新基盤で作成したサロンのため同期はスキップする
- 新基盤から「専用コミュニティ」のAPIを利用してサロン作成データを同期
- 「専用コミュニティ」に新基盤で作成したサロン情報が作成される
- 「専用コミュニティ」のサロン作成のINSERTを検知してLambdaに連携
- 現行システムから新基盤へサロン作成情報の同期処理が動くが、新基盤で作成したサロンのため同期はスキップする
- 「専用コミュニティ」に作成されたサロン情報を「入会・オーナー管理画面」に連携するため、「専用コミュニティ」が「入会・オーナー管理画面」のAPIを実行(現行システムの同期処理)
- 「入会・オーナー管理画面」のデータベースに「専用コミュニティ」のサロン情報と「入会・オーナー管理画面」のサロン情報の紐づけ情報を追加(更新)
- 「入会・オーナー管理画面」のサロン更新のUPDATEを検知してLambdaに連携
- 「入会・オーナー管理画面」のサロン更新データを同期

この一連のプロセスでは、新基盤で行ったサロン登録情報が結果的に同じ新基盤に戻ってくるという事態を招くため、データ不整合が発生しないよう注意を払う必要があります。
また、以下のような通常時は発生しないケースの配慮も必要です。
- イベントベースとバッチベースの同期が重なり合う場合の排他制御
- イレギュラーなデータメンテナンスの同期
- 新基盤の補償トランザクションによってコミットされた情報が打ち消されることを意識した同期
複雑性が増すことで、開発時間が長くなり、リードタイムが長くなる課題がありました。
改善対応
複雑性を低減することが移行プロジェクト成功の鍵と考え、同期処理の数を減らすことを重要な目標としました。
ここで紹介するのは、その一環として実施した対策例の一部です。
ー 初期リリースの同期対象データを絞る
初期リリース時に全てのサロンを新基盤に移行する予定でしたが、設計・実装を進めていくと検討項目が多く、リプレース対応に時間がかかる問題がでてきました。
ステークホルダーと協議の結果、開設審査中のサロンや、所有権譲渡手続き中のサロンなど、一部のステータスを持つサロンの所有者は新基盤に移管できないように制限を追加する仕様に変更しました。
この制限を加えることで、同期パターンを減らすことができ、結果的に同期対応コストを削減することが可能になりました。
ー 現行システムの仕様を変更して同期の発生パターンを共通化する
サロンのキーワードを設定する機能が現行システムの管理画面に2つあり、更新内容が一部異なっていたため、それぞれ同期処理を実装しないといけない状態でした。
調査の結果、現行システムの仕様を変更して、どちらも同じ更新イベントを発生させる方が少ない工数で対応できることが判明したので、現行システムの機能開発や保守を行うプロダクト開発チームと調整し、現行サービス側の仕様を変更しました。
リプレイス中の現行システムは、なるべく仕様変更しないほうが影響少なく移行できますが、現行システム側の仕様変更が可能、且つ現行側で対応したほうが移行対応コストを削減できる場合もあります。
本プロジェクトでは柔軟な対応が可能となるよう、現行システムの修正も選択肢の一つとしました。
ー 現行システムの操作を制限して複雑なデータフローが発生しないようにする
前述の通り、ユーザー情報等の一部のデータは「入会・オーナー管理画面」と「コミュニティサービス」のそれぞれのデータベースで重複して持ち、お互いのユーザーデータを同期し合っています。
詳細は割愛しますが、サロンを運営するスタッフが特定の操作を行うと、ユーザーデータの同期がいっそう複雑になるケースがあり、デグレが発生しやすい状態でした。
少し手順が増えますが、この特定操作と同じ状態にする代替方法が存在する、且つこの特殊な操作が起こるケースは少ないことが判明しました。
現行システムやユーザーへの影響が少なかったので、プロダクトオーナーと特定の操作を制限する方向で仕様を調整し、同期プロセスの複雑さを軽減しました。
ー 同期処理の共通化
一部の同期はイベントベースの同期とバッチベースの同期を別々に作成していたので、同期のコードが長くなり、複雑になっていました。
調査の結果、プレゼンテーション層以降はほぼ同じ処理だったので共通化して保守コストを下げることができました。
これらの対策により、同期のパターンや処理を減らすことができ、同期の対応コストも減らすことができました。
テストの課題
開発中のテスト内容
開発中は以下の方法でテストを行っていました。
ー モックベースのテスト
一般的なモックを使ったテストです。
同期のコードが増えるとモックを書く量も増え、テストコードの作成に時間がかかるようになりました。
このテストですべてのシナリオを網羅することは難しいです。
ー E2E的なテスト
データ同期の変更内容をインプットで渡して、同期先のデータベースの値が期待通り変わっているかを確認するE2E的なテストです。
シナリオテストを作成することも可能ですが、テストコードが長くなるため、このテストですべてのシナリオを網羅することは難しいです。
ー sam local invokeを使ったテスト
イベントベースの同期で利用するLambda関数はAWS SAMで構築しています。
実行環境をシミュレーションしてテストを行いたい場合は、sam local invokeでLambda関数を起動して、同期先のデータベースの値が期待通り変わっているかを確認します。
CDC用のインプットデータ作成等に時間がかかるため、このテストですべてのシナリオを網羅することは難しいです。
ー ステージング環境で手動テスト
実運用で発生し得る、一連の流れの同期のシナリオをスプレッドシートにまとめ、シナリオ通りに現行システムの画面やバッチを手動で操作して、同期先のデータベースの値が期待通り変わっているかを確認するテストです。
パターンは多岐にわたり、認可で利用しているSpiceDBの権限が正しく設定されているか等、データベースの状態を目で見るだけでは確認できない検証もあるため時間がかかりました。
テストの課題について
実際に同期の運用を始めると、いくつかのテスト関連の問題が顕在化しました。
ー テストカバレッジの不足
実装時に全ての同期ケースを洗い出すことが難しく、複雑なパターンのテストコードも書きにくい状態だったため、テストコードは多くありませんでした。
そのため、同期処理に修正を加える際にデグレが起こりやすいという状況が生じました。
ー 時間を要するテストプロセス
新しい基盤の開発途中で現行システムの仕様変更によるスキーマ変更や、新基盤側の課題対応によるスキーマ変更が定期的に発生し、そのたびに同期の修正と時間がかかるテストを行わないといけない状態でした。
リプレースプロジェクトは数年に渡る規模で、現行システムの並行稼働期間はとても長いです。
並行稼働期間中に常にこの問題が続くことは、深刻な問題となってきました。
改善対応
同期の対応コストが今後さらに増えていくことが目に見えてきたので、チーム内で改善検討会を開きました。
テストプロセスの問題点を洗い出し、以下の仮説に基づいたテストフレームワークが、同期対応の効率化に貢献すると考えました。
- 網羅的なシナリオテストを、CI/CDパイプライン上で自動的に実行可能とする
- シナリオの見落としがないよう、直感的に理解しやすいフォーマットで管理する
議論の結果、BDD(ビヘイビア駆動開発)をサポートする、CucumberのGo言語版であるgodogを採用することに決定しました。
採用理由は以下の通りです。
- Goのテストツールとの互換性があり、go testコマンドで簡単にテストを実行できるため、既存のテストコードを活用しつつCIでの自動化が容易
- Gherkin記法を用いた人間が理解しやすいテストシナリオの記述が可能であり、レポーティング機能も充実しているため、ドキュメントとしても機能し、シナリオテストの管理がスムーズに行える
以下は実際のテストコードの一部です。
自然言語で「Given(前提条件)」、「When(イベントやアクション)」、「Then(期待結果)」という形式でシナリオを書けるので理解しやすいです。
Featureファイルにシナリオを自然言語で定義する

Given/When/Thenのテストコードを書く

シナリオとテストコードのマッピングを定義

godogの導入後、テストコードの保守が容易になり、CIにテストを組み込むことで、テスト時間が大幅に短縮されました。
デグレも事前に検知できるため、同期機能の仕様変更を安心して進められるようになりました。
おわりに
DMMオンラインサロンが取り組む「neon」プロジェクトの一環としてのデータ同期の課題と改善策について紹介させていただきました。
複雑性の低減とテスト時間の削減で同期の対応コストを減らすことができました。
同期に限りませんが、開発プロセスとシステムを常にチェックし、持続的に改善を積み重ねる文化が重要だと考えています。
現行システムが並行稼働期間中は常に同期が必要になるので、今後も引き続き改善活動を続けていきたいです。
Cucumberやgodogの詳細についてはまた記事にして共有できればと考えています。
DMMオンラインサロンでは、今後も開発グループの一員として共に働く新たなメンバーを募集しています。
この記事を読んで「neon」プロジェクトに関心をお持ちいただいた方や、さらに深く知りたいと感じた方は、ぜひ気軽にご連絡ください。