Miniflux:开源 SaaS 项目源码笔记
前言 我从 9 月的 27 号开始阅读 Miniflux 项目的代码,阅读成熟的开源项目就像在阅读一本适合自己的书,节奏虽慢但充实。因为快接近阅读这个项目代码的尾声,故整理这篇博文作为过程的记录和分享。 开始之前简单介绍一下 Miniflux。Miniflux 是一个开源的 RSS 订阅服务实现,类似 Tiny Tiny RSS,使用过 RSS 订阅服务的朋友应该不陌生,部署 Miniflux 服务相当于拥有了自己的 Feedly 服务。该项目是基于 Go 和 Vanilla JS (没有使用任何框架的原生 JavaScript API) 的 SaaS APP 实现,提倡尽可能少的外部依赖,简单可维护易扩展,所以这个项目会有很多基础功能的实现(这些细节在参与 Web 框架本身的开发时才会涉及,平时基于 Web 框架的项目开发,比如 Django/Rails 会自带这些功能,直接用就可以)。 本文会通过少量的代码,记录阅读过程中发现的有趣的点,如果你对这些点感兴趣,再去阅读代码即可。我更希望这篇博客像在介绍一本书,让你知道这本书哪里有趣,然后你再自己去判断适不适合你,或者有哪些点适合你。读书不一定要整本通读,发现感兴趣的点,只读感兴趣的部分,也是一种不错的选择。 软件工程应该是件有趣的事,或者说,作为从业者我们应该去发现那些有趣的点,穿透高度抽象带来的认知迷雾。 内容 不使用 ORM 如何实现数据库抽象 如果你想在项目内使用 Golang 标准库的 database/sql 来封装 SQL 操作,可以参考。 Miniflux 中的数据库表的管理,代码分成两类,第一类是对这个表的 SQL 操作的封装,在 storage 包内,不同的表对应不同的 storage 文件夹内的文件,比如 users 表对应 user.go 文件,user.go 中一个操作: // SetLastLogin updates the last login date of a user. func (s *Storage) SetLastLogin(userID int64) error { query := `UPDATE users SET last_login_at=now() WHERE id=$1` _, err := s.db.Exec(query, userID) if err != nil { return fmt.Errorf(`store: unable to update last login date: %v`, err) } return nil } 第二类是对表结构和相关的改动请求的抽象,在 model 包内,不同的表对应不同的 model 包内的文件,比如 users 表对应 user.go 文件,user.go 中的两个抽象: ...