5.1 KiB
Multi App Tenant Architecture
本文定义 hyapp-server 同时服务多个 App 时的租户边界。当前后端不再默认只服务单个 App;popoo、lalu、siya 这类产品都必须先解析成内部 app_code,再进入用户、房间、钱包、活动和 host 业务链路。
Core Rule
app_code 是后端内部租户键。客户端包名、Bundle ID、渠道名和展示 App 名都不能直接作为业务表隔离键。
首个内置映射:
| package_name | platform | app_code | status |
|---|---|---|---|
com.org.laluparty |
任意或空 | lalu |
active |
com.app.huwaa |
任意或空 | huwaa |
active |
com.chat.fami |
任意或空 | fami |
active |
新增 App 时,只新增 apps 注册表记录和对应 App 的基础配置/种子数据,不新建一套服务,也不复制业务代码。
App Registry
apps 表由 user-service 持有,放在 hyapp_user 库:
apps(
app_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
app_code VARCHAR(32) NOT NULL,
app_name VARCHAR(128) NOT NULL,
package_name VARCHAR(191) NOT NULL,
platform VARCHAR(32) NOT NULL DEFAULT '',
status VARCHAR(32) NOT NULL,
created_at_ms BIGINT NOT NULL,
updated_at_ms BIGINT NOT NULL,
UNIQUE KEY uk_apps_code (app_code),
UNIQUE KEY uk_apps_package_platform (package_name, platform)
);
gateway-service 可以通过包名和平台解析 app_code,也可以校验显式传入的 app_code。解析入口是 user-service.AppRegistryService.ResolveApp。
Request Flow
sequenceDiagram
participant C as Client
participant G as gateway-service
participant U as user-service
participant R as room-service / wallet-service / activity-service
C->>G: HTTP request + package/platform/app_code header
G->>U: ResolveApp(package_name, platform, app_code)
U-->>G: active app_code
G->>G: write request context and JWT claims
G->>R: gRPC RequestMeta.app_code
R->>R: all reads and writes are scoped by app_code
Header 约定:
| Header | Meaning |
|---|---|
X-App-Code / X-HY-App-Code / App-Code |
已知内部 app_code |
X-App-Package / X-Package-Name / X-App-Bundle-ID / X-Bundle-ID |
客户端包名或 Bundle ID |
X-App-Platform / X-Platform |
android 或 ios |
已登录请求以 JWT 里的 app_code 为准。未登录登录/注册请求允许通过包名解析;解析成功后,user-service 签发的 token 必须带同一个 app_code。
App Scoped IDs
对客户端暴露的用户长 ID 和房间长 ID 都带 App 前缀,格式为:
<app_code>_<uuid>
例如 com.org.laluparty 解析到 lalu 后,新用户的 user_id 是数值型主键,新房间的 room_id 仍按 App 生成 lalu_<uuid>。服务间主关联使用 users.user_id,JWT 只携带数值型 user_id,gateway 直接把该 ID 透传给后端 RPC。
用户短号 display_user_id 按 App 独立发号,首个号段从 163000 开始。创建房间时 room_short_id 等于创建者当前短号,用于客户端搜索、分享和展示。
Data Boundary
以下业务库表都必须有 app_code,并且查询、唯一键和幂等键都必须带 app_code:
| Service | Database | Scope |
|---|---|---|
user-service |
hyapp_user |
users、登录身份、session、国家、区域、短号、host/Agency/BD 事实 |
room-service |
hyapp_room |
房间元数据、房间列表、快照、命令日志、room outbox |
wallet-service |
hyapp_wallet |
账户、交易、分录、wallet outbox、礼物价格 |
activity-service |
hyapp_activity |
活动状态、事件消费幂等、activity outbox |
规则:
- 跨 App 可以存在相同
display_user_id、相同三方provider_subject、相同钱包command_id;对外长 ID 自带app_code前缀,仍然要求落库唯一键带app_code。 - 业务查询不能只用
user_id、room_id或command_id;必须同时带app_code。 - outbox envelope 必须带
app_code,消费者按事件所属 App 落库和幂等。 - 默认
lalu只用于本地开发和历史测试兜底;真实客户端请求必须能解析出明确 App。
Admin Boundary
后台管理后端使用独立 hyapp_admin 库,不把后台审计表写进 App 业务库。后台操作某个 App 的用户、区域、host、钱包或房间时,HTTP 请求必须显式携带或由后台上下文选择 app_code,再调用对应业务能力。
hyapp_admin.admin_operation_logs 可以在 detail 中记录 app_code、目标对象和变更摘要;不要为了审计把 admin 表复制到各 App 业务库。
Adding A New App
以新增 popoo 为例:
- 在
apps表插入app_code=popoo、包名、平台和状态。 - 为
popoo初始化国家、区域、礼物价格、登录 provider/Firebase 配置。 - 客户端请求携带包名和平台,gateway 解析到
popoo。 - 注册、房间、钱包、活动、host 数据自动按
app_code=popoo写入现有表。
新增 App 不需要拆新微服务;只有当业务规则、部署生命周期或数据合规要求必须强隔离时,才考虑单独部署一套服务集群。