ANTIGRAVITY LABEN
記事一覧/アプリ開発
アプリ開発/2026-05-17上級

課金状態の「正」はどこにある? — Antigravityで設計するad-free Source of Truthパターン

累計5,000万DLアプリ開発で実証した課金状態管理の設計パターン。BillingManager・AdFreeManager・ModalGateの3層設計をAntigravityで実装する実践ガイド。

android27billing3admob16architecture13indie-dev6kotlin6

プレミアム記事

「広告を消す購入をしたのに、まだ表示される」

2026年4月、Beautiful HD Wallpapers(Android版・累計5,000万DL超)のユーザーレビューにこの報告が届きました。購入フロー自体は正常で、Google Play Billing の取引履歴にも記録があります。それでも一部の端末で広告が消えない現象が再現していたのです。

コードを追っていくと、問題の根本が見えてきました。課金状態を判定する条件式が、アプリの5か所に散在していました。それぞれが独立したタイミングで更新され、再起動直後やセッション切り替え時に一時的な矛盾が生じていた。これが原因でした。

2013年にアプリ開発を始めて以来、少しずつ積み上げてきた技術的負債の、ひとつの結晶でした。

この問題を Antigravity と一緒に解決していく中で、「Source of Truth(唯一の真実源)」という設計原則と、それを実現するための3つのクラス——AdFreeManager、BillingManager、ModalGate——の組み合わせにたどり着きました。設計の思考過程と実装の詳細を、以下で共有します。

なぜ「課金状態」は散らばるのか

個人でアプリを長く運営していると、課金状態の判定があちこちに生えていきます。最初はシンプルです。SharedPreferences に is_ad_free フラグを置いて Activity から読む。ところが機能を追加するたびに状況が複雑になっていきます。

リワード広告を導入したとき、「広告を見た人は一定時間だけ ad-free にする」という仕様が加わります。このリワード状態はメモリ上にしか存在しません。次に、購入復元機能を追加したとき、別の判定パスが生まれます。Fragment が増えるたびに、それぞれが微妙に異なる判定ロジックを持つようになります。

こうして気づかないうちに、課金状態は「分散した真実」の集合体になっていきます。それぞれが「正しいはず」なのに、タイミングの違いで矛盾が生まれる。

Antigravity にこの状況を共有したとき、最初の返答はコードの生成ではなく、設計の問いかけでした。「課金状態について、アプリ全体で信頼できる唯一の場所を決めましょう。それが Source of Truth です。どの情報が一番正確だと思いますか?」

この問いかけが、設計の出発点になりました。

Source of Truth という考え方

ソフトウェア設計で「Source of Truth(唯一の真実源)」とは、あるデータについて「これが正しい値だ」と信頼できる唯一の場所を設けるという設計原則です。複数の場所が同じデータを保持し、それぞれが独立して更新される構造は、必然的に矛盾を生みます。

課金状態の場合、Source of Truth の候補は以下の3つが考えられます。

まず、Google Play Billing API の返り値。Google のサーバーに問い合わせた結果であり、最も信頼性が高い情報源です。ただし非同期で時間がかかります。次に、SharedPreferences。ローカルにキャッシュした購入フラグで、高速に読めますが古くなる可能性があります。最後に、メモリ上の変数。リワード広告など一時的な状態を保持しますが、再起動後に消えます。

Antigravity が提案したのは「Google Play Billing を Source of Truth とし、それを包む AdFreeManager クラスに全判定を集約する」というアーキテクチャでした。なぜなら、購入状態は最終的に Google のサーバーが持つ情報が正であり、それ以外はすべてキャッシュや派生値に過ぎないからです。

この考え方を採用することで、「どこに聞けばいいか」が一か所に定まります。判定ロジックが変わるとき、修正は1か所だけです。バグを追うとき、調査は1か所から始まります。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
課金状態が複数箇所に散在してバグが頻発していた問題を、Source of Truthパターンで根本解決できる
BillingManager・AdFreeManager・ModalGateの3層設計を実装し、広告表示とペイウォール制御を一元化できる
Antigravityを活用したアーキテクチャ設計の思考プロセスを習得し、自分のアプリの設計課題に応用できる
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

アプリ開発2026-06-22
壁紙ビューアのスライドショーとページ送りスクラバーを iOS から Android へ逆移植する — SnapHelper との双方向同期でつまずいた点
iOS 版の全画面壁紙ビューアにあったスライドショーと下部スクラバーを Android へ逆移植した実装記録。RecyclerView と SnapHelper で現在ページを確定させ、スクラバーと双方向同期し、自動送りとユーザー操作の競合を状態機械で解いた手順を Kotlin の動くコードで共有します。
アプリ開発2026-06-30
Antigravity に任せた App Open 広告が、課金画面やリワード動画から戻るたびに出てしまう
Antigravity のエージェントに App Open 広告を実装させると、自前のリワード動画や Google Play の課金シートから戻った瞬間にも広告が出ます。AdMob のポリシー違反にも触れるこの挙動を、前面復帰の理由を記録するフォアグラウンド調停役で抑える設計を、動く Kotlin と検証手順つきで解説します。
アプリ開発2026-06-21
戻るボタンで広告が出る/出ないが安定しない — 入れ子のifを独立ガードのリストに作り替えた記録
戻るボタン押下時のインタースティシャル表示判定が、入れ子のif文で優先度が暗黙化して壊れていました。各条件を理由つきの独立ガードに分解し、決定表からテストを生成した設計をまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →