🎄 科学と神々株式会社アドベントカレンダー 2025
License System Day 14: 階層型プランの設計
📖 今日のテーマ
今日は、階層型プラン設計を学びます。
Freemiumビジネスモデルを実現するための3段階プラン(Free / Premium / Enterprise)の設計と実装方法を解説します。
🎯 プラン設計の目的
1. ビジネスモデルの実現
┌─────────────────────────────────────┐
│ Freemiumモデル │
├─────────────────────────────────────┤
│ │
│ Free Tier (フリーミアム) │
│ └─ ユーザー獲得の入口 │
│ │
│ ↓ アップグレード誘導 │
│ │
│ Premium Tier (収益の柱) │
│ └─ メイン顧客層 │
│ │
│ ↓ エンタープライズ提案 │
│ │
│ Enterprise Tier (高収益) │
│ └─ 大口顧客 │
│ │
└─────────────────────────────────────┘
2. ユーザー動線の設計
新規ユーザー
│
├─ Free プラン登録(無料)
│ └─ 基本機能を体験
│ │
│ ├─ 満足して継続利用
│ │
│ └─ 制限に到達
│ └─ Premium検討
│
├─ Premium 契約($9.99/月)
│ └─ 全機能利用
│ │
│ └─ 事業成長
│ └─ Enterprise検討
│
└─ Enterprise 契約($99.99/月)
└─ カスタマイズ対応
📊 3段階プラン詳細設計
Free プラン(無料)
目的: ユーザー獲得・製品体験
# shared/types.nim
const FreePlan* = PlanConfig(
name: "Free",
price: 0.0,
# レート制限
maxRequestsPerHour: 10,
maxRequestsPerDay: 50,
# データ制限
maxMessageLength: 100, # 100文字まで
maxDataStorageMB: 10, # 10MBまで
# 機能制限
features: {
fBasicEcho, # 基本エコーのみ
},
# サポート
support: "Community forum only",
responseTimeSLA: "No SLA",
# その他
ads: true, # 広告表示あり
watermark: true, # ウォーターマーク表示
priorityQueue: false # 優先処理なし
)
制限の理由:
- システムリソース保護
- Premium誘導
- 悪用防止
Premium プラン($9.99/月)
目的: メイン収益源・一般ユーザー
const PremiumPlan* = PlanConfig(
name: "Premium",
price: 9.99,
billingCycle: bcMonthly,
# レート制限
maxRequestsPerHour: 1000,
maxRequestsPerDay: 10_000,
# データ制限
maxMessageLength: 10_000, # 10,000文字まで
maxDataStorageMB: 1000, # 1GBまで
# 全機能利用可能
features: {
fBasicEcho,
fAdvancedEcho, # 大文字変換など
fBulkOperations, # 一括処理
fCustomFilters, # カスタムフィルタ
fAPIAccess, # API直接利用
},
# サポート
support: "Email support",
responseTimeSLA: "24 hours",
# その他
ads: false, # 広告なし
watermark: false, # ウォーターマークなし
priorityQueue: true, # 優先処理あり
# トライアル
trialDays: 14 # 14日間無料トライアル
)
ターゲット:
- 個人ユーザー
- フリーランス
- 小規模チーム
Enterprise プラン($99.99/月)
目的: 高収益・大口顧客
const EnterprisePlan* = PlanConfig(
name: "Enterprise",
price: 99.99,
billingCycle: bcMonthly,
customPricing: true, # カスタム価格対応
# レート制限(実質無制限)
maxRequestsPerHour: int.high,
maxRequestsPerDay: int.high,
# データ制限(実質無制限)
maxMessageLength: 1_000_000, # 1MB
maxDataStorageMB: 100_000, # 100GB
# 全機能 + Enterprise限定機能
features: {
fBasicEcho,
fAdvancedEcho,
fBulkOperations,
fCustomFilters,
fAPIAccess,
fPrioritySupport, # Enterprise限定
fSLA, # SLA保証
fDedicatedAccount, # 専任担当者
fCustomIntegration, # カスタム統合
fOnPremiseDeployment, # オンプレミス対応
},
# サポート
support: "24/7 phone & email",
responseTimeSLA: "1 hour",
dedicatedAccountManager: true,
# その他
ads: false,
watermark: false,
priorityQueue: true,
customBranding: true, # カスタムブランディング
whiteLabel: true # ホワイトラベル
)
ターゲット:
- 大企業
- 政府機関
- 大規模プロジェクト
💻 Nim実装: プラン管理システム
型定義
# shared/types.nim
import std/[sets, times]
type
PlanType* = enum
ptFree = "free"
ptPremium = "premium_monthly"
ptEnterprise = "enterprise_monthly"
BillingCycle* = enum
bcMonthly = "monthly"
bcYearly = "yearly"
bcCustom = "custom"
Feature* = enum
# 基本機能
fBasicEcho
fAdvancedEcho
fBulkOperations
fCustomFilters
fAPIAccess
# Enterprise限定
fPrioritySupport
fSLA
fDedicatedAccount
fCustomIntegration
fOnPremiseDeployment
PlanConfig* = object
name*: string
price*: float
billingCycle*: BillingCycle
customPricing*: bool
# レート制限
maxRequestsPerHour*: int
maxRequestsPerDay*: int
# データ制限
maxMessageLength*: int
maxDataStorageMB*: int
# 機能セット
features*: set[Feature]
# サポート
support*: string
responseTimeSLA*: string
dedicatedAccountManager*: bool
# その他
ads*: bool
watermark*: bool
priorityQueue*: bool
customBranding*: bool
whiteLabel*: bool
trialDays*: int
proc getPlanConfig*(planType: PlanType): PlanConfig =
case planType
of ptFree: FreePlan
of ptPremium: PremiumPlan
of ptEnterprise: EnterprisePlan
proc hasFeature*(plan: PlanConfig, feature: Feature): bool =
feature in plan.features
プラン比較機能
# server/src/plan_manager.nim
import ../../shared/types
import std/[json, tables]
proc comparePlans*(): JsonNode =
## プラン比較表をJSON形式で返す
result = %*{
"plans": []
}
for planType in [ptFree, ptPremium, ptEnterprise]:
let config = getPlanConfig(planType)
result["plans"].add(%*{
"name": config.name,
"price": config.price,
"billing": $config.billingCycle,
"limits": {
"requests_per_hour": config.maxRequestsPerHour,
"requests_per_day": config.maxRequestsPerDay,
"message_length": config.maxMessageLength,
"storage_mb": config.maxDataStorageMB
},
"features": {
"basic_echo": config.hasFeature(fBasicEcho),
"advanced_echo": config.hasFeature(fAdvancedEcho),
"bulk_operations": config.hasFeature(fBulkOperations),
"custom_filters": config.hasFeature(fCustomFilters),
"api_access": config.hasFeature(fAPIAccess),
"priority_support": config.hasFeature(fPrioritySupport),
"sla": config.hasFeature(fSLA),
"dedicated_account": config.hasFeature(fDedicatedAccount),
"custom_integration": config.hasFeature(fCustomIntegration),
"on_premise": config.hasFeature(fOnPremiseDeployment)
},
"support": {
"type": config.support,
"sla": config.responseTimeSLA,
"dedicated_manager": config.dedicatedAccountManager
},
"extras": {
"ads": config.ads,
"watermark": config.watermark,
"priority_queue": config.priorityQueue,
"custom_branding": config.customBranding,
"white_label": config.whiteLabel
},
"trial_days": config.trialDays
})
proc canUpgrade*(currentPlan, targetPlan: PlanType): bool =
## アップグレード可能かチェック
let currentLevel = ord(currentPlan)
let targetLevel = ord(targetPlan)
result = targetLevel > currentLevel
proc getUpgradePath*(currentPlan: PlanType): seq[PlanType] =
## 可能なアップグレードパスを取得
result = @[]
case currentPlan
of ptFree:
result = @[ptPremium, ptEnterprise]
of ptPremium:
result = @[ptEnterprise]
of ptEnterprise:
result = @[] # すでに最上位
プラン選択API
# server/src/main.nim
import jester
routes:
get "/api/v1/plans":
## プラン一覧取得
let plans = comparePlans()
resp Http200, plans
get "/api/v1/plans/compare":
## プラン比較表
let comparison = %*{
"comparison": [
{
"feature": "Requests per hour",
"free": 10,
"premium": 1000,
"enterprise": "Unlimited"
},
{
"feature": "Message length",
"free": "100 chars",
"premium": "10K chars",
"enterprise": "1M chars"
},
{
"feature": "Support",
"free": "Community",
"premium": "Email (24h)",
"enterprise": "24/7 Phone (1h)"
},
{
"feature": "Ads",
"free": true,
"premium": false,
"enterprise": false
}
]
}
resp Http200, comparison
post "/api/v1/subscription/upgrade":
## プランアップグレード
let body = parseJson(request.body)
let token = request.headers["X-Activation-Key"]
let targetPlan = parseEnum[PlanType](body["target_plan"].getStr())
# JWT検証
let claims = cryptoService.verifyJWT(token)
if claims.isNone:
resp Http401, %*{"error": "Invalid token"}
let userId = claims.get["user_id"].getStr()
let currentPlan = parseEnum[PlanType](claims.get["plan_type"].getStr())
# アップグレード可能かチェック
if not canUpgrade(currentPlan, targetPlan):
resp Http400, %*{"error": "Invalid upgrade path"}
# サブスクリプション更新
let success = db.upgradeSubscription(userId, targetPlan)
if not success:
resp Http500, %*{"error": "Upgrade failed"}
# 新しいJWT発行
let newToken = cryptoService.generateJWT(
userId,
claims.get["email"].getStr(),
$targetPlan
)
resp Http200, %*{
"message": "Upgrade successful",
"new_plan": $targetPlan,
"activation_key": newToken
}
🎨 フィーチャーフラグ実装
フィーチャーゲート
# server/src/feature_gate.nim
import ../../shared/types
proc checkFeatureAccess*(plan: PlanConfig, feature: Feature): Result[void, string] =
## フィーチャーアクセスチェック
if plan.hasFeature(feature):
return ok()
else:
return err("Feature not available in " & plan.name & " plan. Please upgrade.")
proc requireFeature*(plan: PlanConfig, feature: Feature) =
## フィーチャー要求(例外を投げる)
let check = plan.checkFeatureAccess(feature)
if check.isErr:
raise newException(FeatureNotAvailableError, check.error)
# 使用例
proc processBulkOperation(plan: PlanConfig, data: seq[string]) =
requireFeature(plan, fBulkOperations) # Freeプランなら例外
# 一括処理実行
for item in data:
processItem(item)
💰 価格戦略
1. 価格設定の考え方
Free: $0/月 → ユーザー獲得コスト削減
Premium: $9.99/月 → 競合他社比較で設定
Enterprise: $99.99/月 → 価値ベース価格設定
2. 年間契約割引
const
PremiumMonthly = 9.99
PremiumYearly = 99.99 # 月額換算 $8.33 (16%割引)
EnterpriseMonthly = 99.99
EnterpriseYearly = 999.99 # 月額換算 $83.33 (16%割引)
3. ボリュームディスカウント(Enterprise)
proc calculateEnterprisePrice(users: int): float =
## ユーザー数に応じた価格計算
let basePrice = 99.99
if users <= 10:
return basePrice
elif users <= 50:
return basePrice * users * 0.9 # 10%割引
elif users <= 100:
return basePrice * users * 0.8 # 20%割引
else:
return basePrice * users * 0.7 # 30%割引
🌟 まとめ
階層型プラン設計の要点:
-
3段階プラン
- Free: ユーザー獲得
- Premium: メイン収益
- Enterprise: 高収益
-
段階的制限
- レート制限
- データ制限
- 機能制限
-
Nim実装
- フィーチャーフラグ
- プラン比較API
- アップグレードフロー
-
価格戦略
- Freemiumモデル
- 年間契約割引
- ボリュームディスカウント
前回: Day 13: レートリミットの実装
次回: Day 15: サーバーサイドの実装(Nim/Jester)
Happy Learning! 🎉