当在大型 React Native 应用中真正需要客户端实体规范化时
Source: Dev.to
背景
在过去的几年里,我在多个 React Native 项目中工作——不同的产品、不同的团队——时常遇到非常相似的症状。
随着应用的增长,同样的实体开始出现在越来越多的地方:
- 信息流
- 详情页
- 搜索结果
- 通知
- 背景更新
每个新功能都会引入一些小而合理的决定:
- 缓存列表响应
- 在屏幕聚焦时重新获取
- 合并局部更新
- 添加派生选择器
- 手动在屏幕之间同步数据
单独来看,这些选择都有道理。整体上,它们都在尝试解决同一个根本问题。到某个时点,这已经不再是偶然。
当归一化看似不必要时
长期以来,只要数据:
- 属于单个屏幕
- 生命周期短暂
- 不会在其他地方复用
在这些情况下,把数据紧贴 API 响应保存完全可行,进行归一化只会增加繁琐而收效甚微。
当归一化变得必要时
问题出现在数据不再是屏幕局部的那一刻。Redux Toolkit 或 React Query 之所以受欢迎是有原因的,但它们的强大恰恰也是它们的宽泛。
我的核心需求要窄得多:在多个屏幕之间共享数据的响应式更新,并保持稳定的身份标识。
-
MobX 在这方面表现极佳。
-
其余的架构随后出现,遵循相当标准的 clean‑code 原则:
- 归一化以避免实体实例重复
- 用显式关系取代嵌套 DTO 树
- 为长期存活的数据设定生命周期边界
- 使用异步编排避免竞争条件
这些都不是事先规划好的。随着时间推移,代码库的增长更多体现在协同逻辑而非功能本身。
数据生命周期挑战
实体不再只属于单个屏幕。缺乏显式规则会导致:
- 内存增长且没有明确的驱逐策略
- 通过遗忘的引用意外保留数据
- 对“谁拥有”数据产生不确定性
当生命周期成为明确关注点时,随着这些关注点被显式化并可组合,出现了真正的转变:
- 垃圾回收策略
- 持久化
- 异步控制(取消、重试、刷新)
- 集成边界
把它们视为可插拔层能够澄清职责。
提炼方法
此时,结构已经不再局限于单个项目。将这种方法抽取成一个小库并非最初的目标;它之所以出现,是因为同样的结构在多个项目中反复出现。它仍然是一个实验性质的实现。
如果你感兴趣,我已经把该方法抽取成一个小型开源实验:
https://github.com/nexigenjs/entity-normalizer
未解之问
我认为这里没有唯一正确的答案。我想了解其他人现在是如何处理的:
- 客户端实体归一化在什么情况下开始对你产生价值?
- 你如何划分服务器缓存与领域实体的界限?
- 你如何处理共享客户端数据的生命周期和所有权?