我给自己的 Go 后台接入了 SQL Server,顺便聊聊 ShiyuAdmin 的架构设计

简介: JavaPub开源的Go+React后台脚手架ShiyuAdmin,新增SQL Server支持,通过GORM多方言抽象实现PostgreSQL/MySQL/SQLite/SQL Server四库统一适配,业务层零修改。架构清晰分层(API→Service→Repository),RBAC权限完备,含用户、菜单、日志等通用模块,并提供各数据库Docker Compose部署方案。(239字)

大家好,我是 JavaPub。

最近又重新折腾了一下我自己的开源后台项目 ShiyuAdmin

项目地址:

https://github.com/Rodert/ShiyuAdmin

这次主要做的一件事情,是给项目补上了 SQL Server 支持

在这里插入图片描述

如果只是站在“能不能连接 SQL Server”的角度来看,这件事情其实非常简单。

Go 里面加一个驱动:

gorm.io/driver/sqlserver

然后:

gorm.Open(sqlserver.Open(dsn))

理论上数据库就连上了。

但对于一个已经存在用户、角色、菜单、权限、日志、数据管理、系统监控等模块的后台脚手架来说,真正的问题并不是:

怎么连接 SQL Server?

而是:

怎么在不改业务代码的情况下,让同一套后台同时跑在 PostgreSQL、MySQL、SQLite 和 SQL Server 上?

这就是这次改造比较有意思的地方。

今天就借这个机会,聊聊现在 ShiyuAdmin 的整体架构,以及 SQL Server 是怎么接进去的。


一、先看一下 ShiyuAdmin 是什么

ShiyuAdmin 本身定位并不是一个具体业务系统。

它更像是一套:

Go + React 的通用后台管理脚手架。

目前后端主要使用:

Go
Gin
Gorm
Viper
JWT
Redis

前端则是:

React
TypeScript
Umi Max
Ant Design Pro
ECharts

项目采用标准的前后端分离模式。后端负责 API、权限、数据库和业务逻辑,前端负责管理后台 UI。

项目现在主要包含这些基础能力:

用户管理
角色管理
菜单管理
部门管理
RBAC 权限
JWT 登录认证
动态菜单
超级管理员
操作日志
Redis 管理
系统监控
数据库管理
数据仪表盘

这些东西本质上都是后台系统绕不开的公共能力。

所以我做这个项目一直有一个思路:

新项目不要每次从登录、权限、用户、菜单重新写。

真正做业务的时候,在这个脚手架上继续增加自己的业务模块就可以了。


二、整体架构其实很清晰

现在 ShiyuAdmin 后端目录大概是这样的:

backend/shiyu-admin-backend
├── cmd
│   └── server
├── configs
├── internal
│   ├── api
│   ├── bootstrap
│   ├── config
│   ├── middleware
│   ├── model
│   ├── repository
│   ├── server
│   └── service
├── migrations
├── pkg
│   ├── database
│   ├── redis
│   ├── jwtutil
│   └── logger
└── sql

如果把它抽象一下,大致可以理解成:

            React / Ant Design Pro
                      │
                      │ HTTP
                      ▼
                  Gin Router
                      │
              Middleware Layer
        JWT / RBAC / Log / Trace
                      │
                      ▼
                  API Layer
                      │
                      ▼
                Service Layer
                      │
                      ▼
              Repository Layer
                      │
                      ▼
                    GORM
                      │
       ┌──────────────┼──────────────┐
       ▼              ▼              ▼
 PostgreSQL         MySQL        SQL Server
                                      │
                                   SQLite

这个结构看起来并不复杂。

但对后台脚手架来说,其实我反而比较喜欢这种架构。

因为:

后台管理系统最大的敌人不是代码少,而是业务越来越多以后所有东西搅在一起。


三、第一层:API 层只负责 HTTP

比如用户管理接口,API 层应该解决的是:

接收参数
参数校验
调用 Service
返回 JSON
HTTP 状态处理

而不应该在 API Handler 里面直接写:

db.Where(...)
db.Create(...)
db.Delete(...)

否则随着业务越来越多,很快就会出现这种代码:

Handler
 ├── HTTP
 ├── SQL
 ├── 权限
 ├── 业务判断
 ├── 数据转换
 └── 日志

最后一个接口可能几百行。

所以 ShiyuAdmin 里面真正的业务逻辑主要继续往下走:

API
 ↓
Service
 ↓
Repository

四、Service 才是业务核心

Service 层做什么?

举个用户管理的例子。

比如:

创建用户

它并不仅仅对应:

INSERT INTO sys_users

还可能涉及:

检查用户名
检查用户编码
密码 BCrypt 加密
部门是否合法
分配角色
数据权限
记录日志

所以这些东西应该属于 Service。

Service 不应该过于关心:

底层到底是 PostgreSQL
还是 MySQL
还是 SQL Server

这也是这次接 SQL Server 能比较顺利的关键原因之一。


五、Repository 才真正负责数据库

再向下一层就是 Repository。

现在 Server 初始化的时候,会统一创建各种 Repository:

authRepo = repoDB.NewAuthRepository(db)
userRepo = repoDB.NewUserRepository(db)
roleRepo = repoDB.NewRoleRepository(db)
menuRepo = repoDB.NewMenuRepository(db)
deptRepo = repoDB.NewDeptRepository(db)

userRoleRepo = repoDB.NewUserRoleRepository(db)
roleMenuRepo = repoDB.NewRoleMenuRepository(db)
roleDeptRepo = repoDB.NewRoleDeptRepository(db)

operationLogRepo = repoDB.NewOperationLogRepository(db)
dbMetaRepo = repoDB.NewDBMetaRepository(db)

然后再注入对应的 Service。

于是就形成了:

User API
   ↓
User Service
   ↓
User Repository
   ↓
GORM

这时候有一个非常重要的好处出现了。

上面的:

API
Service
Repository

基本不用知道底下是什么数据库。

这就是多数据库架构最关键的一点。


六、SQL Server 到底是怎么接进来的?

这次 SQL Server 接入最核心的位置,其实在:

pkg/database/database.go

数据库统一从:

database.Connect(cfg)

进去。

然后根据:

cfg.Database.Driver

决定加载哪个 GORM Dialector。

现在结构大概就是:

switch cfg.Database.Driver {
   

case "postgres", "postgresql":
    dialector = postgres.Open(dsn)

case "mysql":
    dialector = mysql.Open(dsn)

case "sqlite":
    dialector = sqlite.Open(...)

case "sqlserver", "mssql":
    dialector = sqlserver.Open(dsn)

default:
    return nil, errors.Errorf(
        "unsupported database driver: %s",
        cfg.Database.Driver,
    )
}

也就是说:

SQL Server 并没有入侵业务层。

它只是在数据库基础设施层新增了一个 Dialector。

于是上层仍然拿到同样的:

*gorm.DB

后面:

Repository
Service
API

基本不需要发生变化。

我认为这才是一套通用后台应该有的数据库接入方式。


七、为什么我没有到处判断数据库类型

一种比较容易出现的写法是:

if driver == "mysql" {
   

}

if driver == "postgres" {
   

}

if driver == "sqlserver" {
   

}

然后慢慢演变成:

if sqlserver {
   
    ...
} else if mysql {
   
    ...
} else {
   
    ...
}

如果这些判断出现在 Repository、Service 甚至 API 层,整个项目很快就废了。

因为增加一个数据库意味着:

改连接层
改 Repository
改 Service
改 Handler
改初始化

而现在 ShiyuAdmin 的原则更接近:

                    database.Connect
                           │
             ┌─────────────┼─────────────┐
             │             │             │
        PostgreSQL       MySQL       SQL Server
             │             │             │
             └─────────────┼─────────────┘
                           │
                        *gorm.DB
                           │
                    Repository
                           │
                      Service
                           │
                         API

数据库差异尽量截止在:

Database / Repository

这一层。


八、SQL Server 的 DSN 也单独处理了

PostgreSQL 的连接方式一般类似:

host=
user=
password=
dbname=
port=
sslmode=

MySQL 又是另外一套:

user:password@tcp(host:port)/database

SQL Server 则使用:

sqlserver://username:password@host:1433

所以项目里没有强行让所有数据库拼同一种 DSN。

SQL Server 使用 URL 形式构造:

dsnURL := &url.URL{
   
    Scheme: "sqlserver",
    User: url.UserPassword(
        cfg.Database.Username,
        cfg.Database.Password,
    ),
    Host: net.JoinHostPort(
        cfg.Database.Host,
        fmt.Sprintf("%d", cfg.Database.Port),
    ),
}

然后增加:

query.Set("database", cfg.Database.Database)

以及加密配置:

query.Set("encrypt", cfg.Database.SSLMode)

最后:

sqlserver.Open(dsnURL.String())

这样做还有个好处。

账号密码如果带一些特殊字符,不需要自己疯狂处理:

@
:
/
#
?

URL 编码可以减少很多 DSN 拼接问题。


九、数据库连接池还是统一管理

不管使用哪一种数据库,最终都会得到:

sqlDB, err := db.DB()

然后统一设置连接池:

sqlDB.SetMaxOpenConns(50)
sqlDB.SetMaxIdleConns(10)
sqlDB.SetConnMaxLifetime(30 * time.Minute)

所以现在:

PostgreSQL
MySQL
SQL Server
SQLite

在应用层看到的都是:

GORM
  ↓
database/sql
  ↓
数据库驱动

这也意味着后续如果需要做:

连接池配置化
连接超时
慢 SQL
Metrics
数据库健康检查

都可以继续统一放到 Database 层,而不是污染业务代码。


十、配置层也完全独立

SQL Server 已经有单独的配置:

configs/config.sqlserver.yaml

例如:

database:
  driver: "sqlserver"
  host: "shiyu-sqlserver"
  port: 1433
  username: "sa"
  password: "******"
  database: "shiyu_admin_scaffold"
  ssl_mode: "disable"

也就是说切换数据库本质变成:

换配置
而不是改业务代码

这点非常重要。

理想情况下,你甚至可以:

CONFIG_FILE=configs/config.sqlserver.yaml

启动 SQL Server 环境。

换一个配置:

CONFIG_FILE=configs/config.mysql.yaml

就是 MySQL。

整个应用架构不变。


十一、Docker Compose 也拆成了四套数据库模式

目前项目已经不只是代码层支持多数据库,部署层也开始分离。

现在分别提供:

docker-compose.yml
docker-compose.mysql.yml
docker-compose.sqlserver.yml
docker-compose.sqlite.yml

对应:

数据库 使用场景
PostgreSQL 默认生产环境
MySQL 8.4 已有 MySQL 基础设施
SQL Server 2022 企业 SQL Server 环境
SQLite 本地、演示、轻量部署

SQL Server 可以直接:

docker compose -f docker-compose.sqlserver.yml up -d

启动。

这一点我觉得比“代码里支持 SQL Server”更重要。

因为一个开源脚手架真正要降低的是:

从 Git Clone 到系统跑起来之间的成本。


十二、为什么我要支持 SQL Server?

很多做互联网项目的同学可能会问:

都 2026 年了,为什么还专门接 SQL Server?

原因其实很现实。

如果只看互联网创业项目:

MySQL
PostgreSQL

确实已经占据了绝大多数场景。

但是到了:

传统企业
制造业
ERP
医院
学校
政府项目
.NET 老系统
内部管理系统

SQL Server 依然非常常见。

尤其很多企业已经存在大量:

SQL Server
Windows Server
.NET
ERP
OA
MES

基础设施。

你不可能跟甲方说:

为了用我的后台框架,请你们先把数据库换成 PostgreSQL。

这显然不现实。

所以对于一套“通用后台脚手架”来说,多数据库支持其实不是炫技。

而是在解决一个非常现实的问题:

降低接入现有企业基础设施的成本。


十三、GORM 在这里的价值就体现出来了

很多人会讨论:

到底要不要 ORM?

如果是一个非常极致的高性能服务,我可能会倾向于:

sqlc
原生 SQL
手写 DAO

但 ShiyuAdmin 的定位不一样。

它是:

通用中后台脚手架。

这里更重要的目标是:

快速开发
CRUD 效率
代码一致性
多数据库适配
维护成本

GORM 在这个场景里其实非常合适。

比如启动的时候项目统一执行:

db.AutoMigrate(
    &entity.User{
   },
    &entity.Role{
   },
    &entity.Menu{
   },
    &entity.Dept{
   },
    &entity.UserRole{
   },
    &entity.RoleMenu{
   },
    &entity.RoleDept{
   },
    &entity.OperationLog{
   },
    ...
)

核心 RBAC 表可以直接根据 Entity 初始化。

所以:

同一套 Entity
      ↓
     GORM
      ↓
PostgreSQL / MySQL / SQL Server / SQLite

这让多数据库维护成本低了非常多。


十四、RBAC 其实也是整个项目最核心的一层

数据库之外,我觉得 ShiyuAdmin 另一个比较核心的设计就是 RBAC。

目前整体关系可以理解成:

User
  │
  ▼
UserRole
  │
  ▼
Role
  │
  ├────────► RoleMenu ────────► Menu
  │
  └────────► RoleDept ────────► Dept

也就是:

用户
 ↓
角色
 ↓
菜单 / 权限

同时角色还可以继续关联:

部门
数据范围

这也是为什么系统不仅可以做到:

这个用户能不能看到菜单

还可以继续向:

这个用户能不能调用 API
这个角色可以看哪些部门
这个角色可以操作哪些数据

扩展。

项目的 JWT 中还单独加入了:

IsSuperAdmin bool

超级管理员可以直接绕过普通 RBAC 权限检查。普通账号则继续根据用户、角色、菜单关系计算权限。

这个设计对于后台系统很实用。

因为一定要给运维留一条:

“最高权限救场通道”。


十五、应用启动过程其实也体现了架构

现在启动过程大概是:

main.go
   ↓
加载 Config
   ↓
server.Run()
   ↓
初始化 Logger
   ↓
初始化 Gin
   ↓
database.Connect()
   ↓
AutoMigrate()
   ↓
初始化默认管理员
   ↓
初始化 RBAC
   ↓
初始化 Redis
   ↓
创建 Repository
   ↓
创建 Service
   ↓
注册 API
   ↓
启动 HTTP Server

Server 层实际上扮演的是一个:

依赖组装器。

数据库、Repository、Service 都在这里完成组合。

所以业务代码本身不会到处:

gorm.Open(...)
redis.NewClient(...)

所有基础设施集中初始化。

这一点对于后面做:

Mock
单元测试
替换数据库
增加缓存
增加 MQ

都会比较舒服。


十六、这次 SQL Server 改造,我最满意的并不是“支持 SQL Server”

如果只是:

✔ SQL Server

其实没有什么技术含量。

真正让我觉得这次改造有价值的是:

SQL Server 加进来以后,原来的 API、Service、RBAC 和大部分 Repository 并没有因此被破坏。

换句话说:

增加一个基础设施

没有演变成:

重写一遍业务系统。

这其实就是架构设计真正有价值的地方。

好的架构不是:

看起来用了多少设计模式。

而是:

当需求发生变化的时候,需要改多少地方。


十七、现在这套架构可以继续怎么演进?

接下来其实还有几个方向可以继续完善。

1. 数据库能力矩阵

不同数据库并不是百分百一样。

以后可以做一份明确的:

PostgreSQL   ✅
MySQL        ✅
SQL Server   ✅
SQLite       ✅

然后针对:

表注释
字段类型
分页
JSON
全文索引
事务
DDL
元数据查询

分别测试兼容性。


2. 数据库差异继续下沉

未来如果出现:

SQL Server 特有 SQL
PostgreSQL 特有 SQL
MySQL 特有 SQL

不要让它进入 Service。

而应该继续下沉到:

Repository
DBMetaRepository
Dialect Adapter

例如:

DBMetaRepository
       │
       ├── PostgreSQL Metadata
       ├── MySQL Metadata
       └── SQL Server Metadata

这样数据管理、表结构查看这些功能才能真正做到跨数据库。


3. 连接池配置化

现在连接池参数是统一配置的:

MaxOpenConns = 50
MaxIdleConns = 10
ConnMaxLifetime = 30min

未来最好进一步配置化:

database:
  max_open_conns: 50
  max_idle_conns: 10
  conn_max_lifetime: 30m

因为不同部署规模的数据库并不应该使用完全相同的参数。


4. 把多数据库加入 CI

这是我认为非常重要的一步。

以后提交代码的时候最好直接跑:

PostgreSQL Test
MySQL Test
SQL Server Test
SQLite Test

否则:

“理论兼容”

和:

“每次 Commit 都验证兼容”

完全是两回事。


十八、最后

ShiyuAdmin 从一开始我就没有想把它做成一个特别重的“大而全框架”。

我的目标一直比较简单:

把后台项目里重复率最高的那 60%~80% 基础工作提前做好。

例如:

登录
JWT
RBAC
用户
角色
菜单
部门
日志
Redis
监控
数据库
Docker
前端后台

这些东西一旦稳定下来,以后真正开发业务系统的时候,就可以直接:

写业务。

而不是每开一个新项目:

第 1 天:写登录
第 2 天:写用户
第 3 天:写角色
第 4 天:写菜单
第 5 天:写权限
……

这次加入 SQL Server,也是沿着这个方向继续走。

现在底层已经可以覆盖:

PostgreSQL
MySQL
SQL Server
SQLite

我觉得对于一套 Go 后台脚手架来说,数据库层基本已经有了一个比较完整的雏形。SQL Server 2022 也已经有独立的 Docker Compose 部署方案。

后面我还会继续完善:

多数据库兼容
数据权限
代码生成
文件管理
系统监控
部署能力
自动化测试

如果你平时也在做:

Go
Gin
GORM
React
企业后台
RBAC
SQL Server

可以看看这个项目。

services:
  shiyu-sqlserver:
    image: mcr.microsoft.com/mssql/server:2022-latest
    platform: linux/amd64
    container_name: shiyu-sqlserver
    restart: unless-stopped
    environment:
      ACCEPT_EULA: "Y"
      MSSQL_PID: "Developer"
      MSSQL_SA_PASSWORD: "ShiyuAdmin@123"
    ports:
      - "11433:1433"
    volumes:
      - shiyu-sqlserver-data:/var/opt/mssql
    healthcheck:
      test: ["CMD-SHELL", "timeout 2 bash -c '</dev/tcp/127.0.0.1/1433'"]
      interval: 10s
      timeout: 5s
      retries: 15
      start_period: 30s
    networks: [shiyu-network]

  shiyu-sqlserver-init:
    image: mcr.microsoft.com/mssql-tools18:latest
    platform: linux/amd64
    container_name: shiyu-sqlserver-init
    restart: "no"
    environment:
      MSSQL_SA_PASSWORD: "ShiyuAdmin@123"
    depends_on:
      shiyu-sqlserver:
        condition: service_healthy
    command:
      - /bin/bash
      - -c
      - >
        /opt/mssql-tools18/bin/sqlcmd -S shiyu-sqlserver -U sa -P "$$MSSQL_SA_PASSWORD" -C
        -Q "IF DB_ID(N'shiyu_admin_scaffold') IS NULL CREATE DATABASE [shiyu_admin_scaffold]"
    networks: [shiyu-network]

  shiyu-redis:
    image: redis:7
    container_name: shiyu-redis
    restart: unless-stopped
    volumes:
      - shiyu-redis-data:/data
    command: redis-server --appendonly yes
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks: [shiyu-network]

  shiyu-app:
    image: ${SHIYU_IMAGE:-ghcr.io/rodert/shiyuadmin:latest}
    container_name: shiyu-app
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      CONFIG_FILE: configs/config.sqlserver.yaml
    ports:
      - "18000:80"
    depends_on:
      shiyu-sqlserver-init:
        condition: service_completed_successfully
      shiyu-redis:
        condition: service_healthy
    networks: [shiyu-network]

networks:
  shiyu-network:
    driver: bridge

volumes:
  shiyu-sqlserver-data:
  shiyu-redis-data:

image.png

我是王仕宇

GitHub:

https://github.com/Rodert/ShiyuAdmin

也欢迎一起提 Issue、PR,或者把它直接拿去作为自己项目的基础骨架。

一起成为勇猛精进的人类。

目录
相关文章
|
存储 缓存 编解码
Web端短视频编辑器的设计与实现 - 像做PPT一样做视频
对于视频的生产,一般的方案是交由专业机构去创作,但这将花费很多预算,如果我们能提供一个工具,基于知识的通用结构沉淀一些视频模版,让用户快速创作出视频知识内容岂不美哉?让想法再奔放些,如果我们能直接从知识库中抽取结构化的知识内容直接生成视频或是半成品视频,用户只需要稍作调整就能发布,这想想就很酷吧?是的,小蜜视频创作工具我就是想做这样一件事情。本篇分享来自阿里巴巴前端工程师李志成(敦固)在第十六届D2前端技术论坛的分享。
4170 0
Web端短视频编辑器的设计与实现 - 像做PPT一样做视频
|
Java BI 开发工具
静态代码自动扫描p3c的使用
静态代码自动扫描p3c的使用
1688 0
|
5月前
|
Linux 测试技术 开发者
【开源剪映小助手】开发者指南
capcut-mate 是开源剪映自动化工具,基于 FastAPI + Electron 构建,支持跨平台草稿管理、媒体处理与视频导出。采用分层架构、条件依赖与优雅降级机制,确保 Windows/Linux 兼容性与一致开发体验。(239字)
|
18天前
|
人工智能 JavaScript API
DeepSeek Harness 实测:大模型为什么还需要 Harness?
本文深度评测DeepSeek Harness——国产AI编程Agent新工具。作者实测三大任务(幸存者游戏、俄罗斯方块、梁子滑动变阻器),对比GPT/Codex,分析完成时间、Token消耗、费用与效果,并详解其“模型为脑、Harness为手脚”的执行闭环机制及蓬勃发展的插件生态(文件引用、多模态、数据库等)。
349 7
DeepSeek Harness 实测:大模型为什么还需要 Harness?
|
2月前
|
人工智能 搜索推荐 API
什么是 Ontology?用一个电商例子讲清楚“本体论”
Ontology(本体)在AI中并非哲学玄谈,而是对领域知识的结构化定义:明确概念、关系、属性与规则,为机器提供可理解、可推理的“知识骨架”,赋能RAG、知识图谱、AI Agent等场景。(239字)
553 1
|
16天前
|
人工智能 Java API
本体相关的开源项目有哪些?从 Ontology 到 Knowledge Graph,再到 AI Agent
本文深入探讨“本体(Ontology)”在AI新时代的核心价值,聚焦其如何为Agent构建可理解、可推理的业务世界模型。梳理12个关键开源项目(如Protégé、Owlready2、Ontop、Graphiti等),覆盖本体建模、知识图谱、虚拟图谱、LLM驱动构建与Ontology-RAG等前沿方向,揭示Ontology正从学术概念跃升为AI Agent的“业务操作系统”。
448 0
|
3月前
|
SQL 人工智能 IDE
从个人生产力到组织能力:LoongSuite-Pilot×SLS 的 AI Coding 度量实践
本文介绍如何通过 LoongSuite-Pilot 采集异构 AI Coding Agent 事件流,结合 SLS 大盘的 SQL 分析能力,构建从个人使用行为到组织级度量的完整看板,帮助研发团队量化 AI 工具的实际落地效果。
455 24
|
24天前
|
人工智能 安全 前端开发
基于 AgentScope 构建金融级智能体底座实战
金融级 AI 原生智能体底座白皮书发布。
|
2月前
|
人工智能 自然语言处理 安全
如何让你的 codex 生成图片和修改图片的 skill
Codex中文网站长宇哥介绍:通过安装Rodert的ChongPlus图片Skill,Codex可直接用自然语言生成/修改图片,无需写代码或调用API。只需提供GitHub仓库和API Key,即可实现文字生图、参考图编辑等能力,大幅提升AI图像创作效率。(239字)
688 0
|
2月前
|
SQL 人工智能 架构师
GPT-5.6 Sol、Terra、Luna 怎么选?听我一句,别一上来就用最贵的
本文详解GPT-5.6三大模型(Luna/Terra/Sol)的差异化定位:Luna适合批量简单任务,Terra是日常开发写作的性价比首选,Sol专攻高成本、强推理的复杂攻坚。倡导“按需选模”,而非盲目追求最强——AI使用成熟度,正体现在懂得何时省、何时投。(239字)
1087 2