有了pageconvert为什么还要pagefacade
这是 leijmdas 全家桶设计中的一个关键分层问题。PageConvert 和 PageFacade 不是替代关系而是职责分离 组合复用的设计---核心区别维度 PageConvert[T, U] PageFacade[T, U]定位 分页参数转换器底层组件 分页查询外观统一入口职责 只处理分页条件limit/offset/order 整合分页 结果 字段控制 脱敏 权限嵌入方式 被 PageFacade 嵌入 组合 PageConvert ResultConvert使用场景 单独处理分页 SQL 构建 完整分页查询链路API 复杂度 低仅分页相关 高链式配置全部能力---为什么要拆两层1. 单一职责原则SRP// PageConvert 只关心分页参数 → SQL 片段type PageConvert[T, U any] struct {page intsize intsorts []SortField}func (pc *PageConvert[T, U]) ToSQL() (string, []any) {return LIMIT ? OFFSET ?, []any{pc.size, (pc.page-1)*pc.size}}// ResultConvert 只关心 T → U 转换 字段过滤type ResultConvert[T, U any] struct {optionFields OptionFieldsmasker Masker}func (rc *ResultConvert[T, U]) Convert(src T) (U, error) { ... }如果合并成一个类一个修改如调整脱敏规则可能影响分页逻辑违反 SRP。2. 组合优于继承// PageFacade 是组合器不是继承器type PageFacade[T, U any] struct {PageConvert[T, U] // 嵌入复用分页能力ResultConvert[T, U] // 嵌入复用转换能力}// 这样 PageFacade 可以// 1. 直接暴露 PageConvert 的方法如 WithPage/WithSize// 2. 直接暴露 ResultConvert 的方法如 WithExclude/WithMaskRule// 3. 自己新增跨组件协调方法如 WithPermissionGo 没有继承只有组合。PageFacade 通过嵌入Embedding把两个独立组件的能力聚合到一个统一入口。3. 独立复用场景场景 A只需要分页不需要转换// 内部服务间调用直接返回实体不需要 VO 转换var pc pagemodel.PageConvert[entity.User, entity.User]pc.WithPage(1).WithSize(20)sql, args : pc.ToSQL()场景 B只需要结果转换不需要分页// UISimpleQuery 查询单条详情用到 ResultConvert 但不用 PageConvertrc : pagemodel.NewResultConvert[entity.User, vo.UserVO]()rc.WithExclude(password).WithMaskRule(phone, rule)vo, _ : rc.Convert(userEntity)场景 C完整分页查询PageFacade// 面向前端的标准分页接口需要全部能力facade : pagemodel.NewPageFacade[entity.User, vo.UserVO]().WithPage(1).WithSize(20). // PageConvert 能力WithExclude(password). // ResultConvert 能力WithMaskRule(phone, rule). // ResultConvert 能力WithPermission(ctx) // PageFacade 协调层能力---架构分层示意┌─────────────────────────────────────────┐│ PageFacade[T, U] │ ← 统一入口面向 Handler│ ┌─────────────────────────────────┐ ││ │ Coordination Layer协调层 │ ││ │ • WithPermission(ctx) │ │ ← 跨组件逻辑权限→字段过滤脱敏│ │ • QueryPage(fn) │ │ ← 编排执行流程│ └─────────────────────────────────┘ ││ ┌─────────────────────────────────┐ ││ │ PageConvert[T, U]分页转换器 │ │ ← 独立组件可单独使用│ │ • page/size/sort → SQL │ ││ │ • OptionFields分页字段过滤 │ ││ └─────────────────────────────────┘ ││ ┌─────────────────────────────────┐ ││ │ ResultConvert[T, U]结果转换器│ │ ← 独立组件可单独使用│ │ • T → U 映射 │ ││ │ • OptionFields结果字段过滤 │ ││ │ • Masker脱敏 │ ││ └─────────────────────────────────┘ │└─────────────────────────────────────────┘---关键设计决策问题 如果只有 PageConvert 拆分 PageConvert PageFacadeSimpleQuery 需要脱敏怎么办 在 PageConvert 里加 Masker破坏单一职责 ResultConvert 独立提供SimpleQuery 直接复用分页参数和返回结果字段过滤不一致 一个 OptionFields 难以区分 PageConvert 和 ResultConvert 各自独立 OptionFields单元测试分页逻辑 必须连带测试转换脱敏 单独测试 PageConvertMock 更简单其他场景复用转换能力 硬耦合在分页结构里 ResultConvert 可被任何查询场景复用---一句话总结 PageConvert 和 ResultConvert 是零件PageFacade 是组装好的整机。 你可以只用零件内部服务也可以用整机面向前端取决于场景复杂度。这与 Go 的小接口、大组合哲学一致leijmdas 没有做一个万能的大类而是拆成可独立使用的小组件再用 PageFacade 按常见场景预组装。