Wails2 系列 04:存储层与配置层:把数据读写从业务服务中剥离
cbowen

适合读者

适合已经在 Wails 项目中读写本地文件、JSON、SQLite 或配置文件,并开始担心数据逻辑散落的开发者。

问题场景

桌面应用通常离不开本地数据:用户设置、缓存、业务记录、授权信息、导入导出文件。Wails 项目因为 Go 侧读写文件很方便,所以很容易在业务服务里随手写:

1
2
os.ReadFile(...)
os.WriteFile(...)

一开始这没什么,后来问题会集中爆发:清空数据不知道该删哪些文件,导入导出漏掉某些目录,测试时需要真实用户目录,迁移数据时到处找读写点。

项目观察

当前项目有两个值得观察的边界:

  • internal/storage:偏业务数据持久化,例如列表、记录、统计等。
  • internal/config:偏应用配置、本地数据目录、导入导出和清理。

同时,应用服务中会调用这些能力完成“导出用户数据”“导入用户数据”“清空数据”这样的用例。服务负责流程,底层包负责数据细节。

拆分原则

配置和业务数据要分开。

配置通常回答这些问题:

  • 应用数据目录在哪里?
  • 主题、启动选项、窗口偏好是什么?
  • 哪些文件应该被导入导出?
  • 哪些文件清空数据时必须保留?

业务存储通常回答这些问题:

  • 待办如何保存?
  • 用户资料如何查询?
  • 统计数据如何计算?
  • 数据结构如何迁移?

业务服务不应该关心某个记录最终是 JSON、SQLite 还是 BoltDB。它只应该调用接口表达用例。

改造示例

不推荐在服务里直接散落文件操作:

1
2
3
4
5
6
func (s *TodoService) Save(title string) error {
path := filepath.Join(userHome(), "todos.json")
data, _ := os.ReadFile(path)
// parse, append, marshal
return os.WriteFile(path, data, 0644)
}

可以把存储独立出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
type TodoStore interface {
List() ([]Todo, error)
Save(todo Todo) error
}

type TodoService struct {
store TodoStore
}

func (s *TodoService) Create(title string) error {
todo := Todo{Title: title, Done: false}
return s.store.Save(todo)
}

配置也可以集中管理:

1
2
3
4
5
6
7
8
9
type Config struct {
Theme string
DataDir string
}

func LoadConfig() (*Config, error) {
// 只在这里处理配置文件路径、默认值和兼容逻辑
return &Config{Theme: "system"}, nil
}

当你要做导入导出时,也应该集中处理策略:

1
2
3
4
5
6
func ExportUserData(dataDir string, output string) error {
// 收集允许导出的文件
// 跳过锁文件、临时文件和授权文件
// 打包为 zip
return nil
}

检查清单

  • 业务服务里是否还散落大量 os.ReadFileos.WriteFile
  • 配置数据和业务数据是否分别有负责模块?
  • 数据目录是否由统一位置决定?
  • 导入导出是否有明确的包含和排除规则?
  • 清空数据是否能复用同一套数据边界?
  • 存储逻辑是否可以脱离 Wails context 测试?
  • 业务服务是否通过接口或构造函数依赖存储层?

下一篇

下一篇会转到前端:即使 Go 后端拆清楚了,如果页面直接到处调用 wailsjs,前端仍然会很快变乱。

 评论
评论插件加载失败
正在加载评论插件