介绍
这一篇讲为什么这样设计,以及它不适合什么场景。
如果你只想跑起来,去看安装。
三个核心决策
决策一:把「域」做成第一等公民
问题:PC 站、移动 App、小程序、后台——四个端的接口形态不同,但业务规则相同。
多数框架里这会变成:
// 每多一个端,就多一个 controller
class WebPostController { /* 复制一遍 */ }
class AppPostController { /* 复制一遍 */ }
class MiniappPostController { /* 复制一遍 */ }或者:
// 一个 controller 里到处判断
if ($platform === 'web') { ... }
elseif ($platform === 'app') { ... }这个框架的做法:
// config/api.php
'domains' => [
'admin' => ['prefix' => 'admin', 'versions' => ['v1'], 'middleware' => ['auth']],
'web' => ['prefix' => 'web', 'versions' => ['v1']],
'app' => ['prefix' => 'app', 'versions' => ['v1']],
],每个域独立前缀、独立版本、独立中间件,共用同一批领域服务:
// 三个域的控制器都调同一个 PostService
return $this->posts->paginate(page: 1, perPage: 20);额外的好处:token 天然不跨域。
web 域签发的 token 访问 admin 域会返回 401——因为 token 里绑定了域标识。这省掉了大量「这个用户能不能访问这个接口」的判断。
代价:域的划分需要前期想清楚。如果一开始只做一个域,后来要拆,路由要重组。
决策二:零运行时依赖
问题:Composer 生态很强大,但也带来部署摩擦。
- 协作时
composer.lock冲突 - 生产环境要装 Composer 或传
vendor/ - 依赖漏洞要跟踪升级
- 一个包更新可能连锁影响十几个包
这个框架的做法:核心不用任何第三方包。
$ grep -r '^use [A-Z]' core/ | grep -v SunsetReverie
(无输出)代价是自己实现了不少东西:PSR-4 自动加载、容器、查询构造器、JWT、SMTP 客户端。
取舍点:
| 自己实现的好处 | 代价 |
|---|---|
| 出问题能立刻定位到具体代码 | 功能不如成熟轮子完善(比如 SMTP 不支持 TLS) |
| 部署零摩擦 | 某些边界情况需要自己踩(比如三库方言差异) |
| 没有版本升级焦虑 | 性能优化要自己做 |
边界写清楚:SMTP 不支持 TLS 是刻意的。证书校验、SNI、协议降级手写极易出错,而发邮件 90% 走内网 relay 或第三方 API。需要加密时部署本地 relay 转发。
决策三:不做 ORM
问题:ORM 让简单查询变简单,让复杂查询变复杂。
// ORM 风格
$posts = Post::with('author', 'tags')
->whereHas('comments', fn ($q) => $q->where('status', 1))
->get();这段代码生成了几条 SQL?只有跑一遍才知道。
N+1 问题在开发环境感知不到(本地数据库 1ms),上线后才发现(远程 5ms × 200 次 = 1 秒)。
这个框架的做法:
$posts = DB::table('posts')
->select('id', 'title', 'author_id')
->where('status', 1)
->limit(20)
->get();
// 作者单独查一次,而不是每行查一次
$authors = DB::table('users')
->whereIn('id', array_column($posts, 'author_id'))
->get();SQL 在眼前,查了几次库一目了然。
代价:关联查询要自己写。一对多、多对多不会自动帮你处理。
为什么接受这个代价:真实业务里,复杂查询往往需要针对索引优化,ORM 生成的 SQL 未必合适。而简单查询手写并不麻烦。
它不适合什么
诚实地说:
| 场景 | 为什么不适合 |
|---|---|
| 需要庞大生态 | 没有现成的管理后台、CMS、支付集成 |
| 重度关联业务 | 十几个表互相关联时,手写 JOIN 会很痛苦 |
| 团队已熟悉主流框架 | 学习成本 vs 收益不划算 |
| 需要开箱即用 | 这里只有骨架,业务要自己写 |
| 需要复杂权限体系 | 只提供了中间件 + 服务内判断,没有 Gate/Policy |
它适合什么
| 场景 | 为什么 |
|---|---|
| 多端共存的业务后端 | 域隔离直接解决「四套 controller」的问题 |
| 一个人维护的项目 | 没有魔法,出问题能快速定位 |
| 部署环境受限 | 不能装 Composer 也能跑 |
| 想读懂每一行执行路径 | 55 个 core 文件,全都能读完 |
| 团队规模小 | 少一层抽象,沟通成本低 |
和其他框架的定位差异
| 夕梦 | Laravel | Slim | |
|---|---|---|---|
| 依赖 | 零运行时依赖 | 大量第三方包 | 少量依赖 |
| ORM | 无 | Eloquent | 无(自带 PDO 封装) |
| 多域 | 内置 | 需自行组织 | 需自行组织 |
| 生态 | 无 | 极丰富 | 中等 |
| 学习曲线 | 中(需理解域与分层) | 中 | 低 |
| 适合规模 | 中型 | 任意 | 小型 |
不是替代关系。如果你的项目适合 Laravel,就用 Laravel——它的生态能省下大量时间。
这个框架的存在理由是:当项目形态是「多端 + 小团队 + 不想被依赖绑架」时,它比主流框架更合适。
设计原则
框架内部遵循几条硬规则,由 sr lint:architecture 自动检查:
| 编号 | 约束 |
|---|---|
| ARCH-1 | 内核不得引用业务命名空间 |
| ARCH-2 | 内核不得引用第三方包 |
| ARCH-3 | 组件之间无未声明的互相依赖 |
| ARCH-4 | 内核仅组装层(Application / Facades)依赖组件 |
| ARCH-7 | Facade 的 @method 与底层类一致 |
| ARCH-8 | 组件目录与 autoload 映射表一致 |
不通过即阻断合并。这几条的价值在于:它们约束的是「依赖方向」,而依赖方向一旦腐烂,重构成本会指数上升。