做小程序第一天,我花最多时间的不是写代码,而是定一个东西:数据模型的最顶层实体,到底是「宝宝」还是「家庭」。
这个决定看起来小,但它决定了后面所有功能的开发成本。我选了「家庭」,后来加「邀请家人」「多孩切换」「长辈查看」时,一行历史数据都不用改。这篇讲清楚这个模型的设计思路和落地细节。
一、为什么是「家庭」而不是「宝宝」或「个人」

直觉上,宝宝记录 App 的核心是「宝宝」,数据都挂在宝宝下面就行。但实际的协作场景是:
- 爸爸喂奶记一条,妈妈想立刻看到
- 奶奶白天带娃,记了睡眠,晚上爸妈要能查
- 过两年生二胎,两个孩子的数据要分开又能同账号切换
如果顶层是「个人」,那共享就是事后的补丁,得做复杂的授权和同步。如果顶层是「宝宝」,那一个宝宝被多人记录时,权限和归属就乱了。
所以正确的抽象是:数据属于一个家庭,家庭里有成员,成员围着宝宝记 。顶层实体是 families(家庭),宝宝挂在家庭下,所有记录通过 familyId 关联。
css
families(家庭)
├─ _id, name, createdBy, memberOpenids[]
└─ babies(宝宝)── familyId
└─ records / events / media / purchases ... ── familyId + babyId

二、两个字段,从第一天就埋下「协作」的种子
很多家庭类产品是「先单机跑通,再加共享」,结果加共享时要大改。我反着来------第一天就在 families 集合里放了两个字段:
javascript
{
name: '宝宝的家',
createdBy: OPENID, // 谁创建的
memberOpenids: [OPENID], // 家庭成员列表(数组,天然支持多人)
createdAt: Date.now()
}
关键在 memberOpenids 是个数组 。第一天它只有一个元素(创建者自己),但结构天生支持多成员。等后面做「邀请家人」时,只是往这个数组里 push 一个新的 openid,再把它加进查询条件------历史数据零迁移。
查询时用 where({ memberOpenids: OPENID }),意思是「只要我是这个家庭的成员之一,就能查到」。这条查询从单机到多人,写法一个字没变。

三、多孩、找回、半途失败,都在 initFamily 里收口
建档入口我用一个云函数 initFamily 统一收口,它是幂等的(同一个 openid 重复调用,返回已有档案,不会重复建)。它要处理三种真实情况:
javascript
exports.main = async (event) => {
const { OPENID } = cloud.getWXContext()
const fams = await db.collection('families')
.where({ memberOpenids: OPENID }).limit(10).get()
for (const family of fams.data) {
// 多孩:返回该家庭全部宝宝,前端做切换
const babies = await db.collection('babies')
.where({ familyId: family._id }).limit(20).get()
if (babies.data.length) {
return { ok: true, familyId: family._id,
baby: babies.data[0], babies: babies.data, reused: true }
}
}
// 没找到家庭 → 查找模式到此为止,让前端去走创建页(不擅自创建)
if (!trimmedName) return { ok: true, reused: false, needCreate: true }
// 创建模式:家庭存在但没宝宝(上次半途失败)→ 补建宝宝;否则整家新建
}
三个设计点:
- 多孩 :返回
babies数组给前端做切换,同时保留baby(取第一个)兼容旧调用方------老代码不用改。 - 查找 vs 创建分离 :没带
name就只查不建。这是「清缓存/换机后自动恢复」的关键------用户打开小程序,先静默查一遍,有档案直接进,没有才弹创建页。 - 半途失败兜底:上次建到一半失败(家庭建了、宝宝没建),下次进来会走「补建宝宝」分支,不会出现死档。

四、AI 任务单独一张表:aiJobs
除了业务数据,还有一类容易被忽略的数据:AI 异步任务。比如 AI 生图、语音问诊,这些都不是秒级返回的,得异步跑。
我没有把它们散落在各业务表里,而是单独建了一张 aiJobs 任务表统一调度:谁发起的、什么类型、输入是什么、状态(排队/进行中/成功/失败)、结果存哪。好处是所有 AI 能力用同一套排队和重试机制,加新 AI 功能时不用重造轮子。

五、给独立开发者的三条建模建议
- 先想清楚「数据属于谁」。是单用户独享,还是天然多人共享?这个答案决定顶层实体,且几乎无法事后低成本更改。
- 把「将来要多人」的字段第一天就设计成数组。数组 vs 单值的差别,就是「零迁移」和「大改」的差别。
- 入口函数做成幂等的。网络会重试、用户会连点、上次会半途失败------幂等的入口能把这三类问题一次性消化掉。
数据模型是地基。地基第一天歪一点,后面每层都得找补;第一天想对了,后面全是顺水推舟。
如果这篇对你有用,欢迎关注 看「AI 工具人 PM 实战」系列更新;你做项目时有没有过「第一天的一个设计决定,后来救了你」的经历,评论 聊聊;觉得有用就收藏备用。
下一篇预告:《微信云开发权限实战:读写全走云函数 + 集合免手动建的自愈模式》------讲那个让我档案打不开的权限大坑。