恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

WuKongIM 分层架构重构指南:用 Access / Usecase / Runtime 三层替代 internal/service

  • 首页
  • 资讯中心
  • /
  • WuKongIM 分层架构重构指南:用 Access / Usecase / Runtime 三层替代 internal/service

相关资讯

Claude 2026/10/15 3:40:12
YOLO-Series ONNXRuntime Rust 推理实战:用 usls 打通 YOLOv5–YOLO11 全任务的 Rust 部署 2026/10/15 3:40:12
Folia 播放器快速上手:8 分钟跑通搜索、播放与全屏歌词动画 2026/10/15 3:35:11

最新资讯

Android音视频开发基础:从码率、GOP到FFmpeg命令的完整梳理
环形链表II详解:快慢指针找环入口的Floyd算法与Java实现
Flutter for OpenHarmony:家具记录App添加房间功能开发实录
编译期与运行期:Java开发必懂的阶段划分与核心机制解析
HTTP请求方法与状态码详解:语义、易混淆点与排障实战
思科OptionB双出口互联方案:eBGP多跳与路由反射器实战

今日推荐

AI视频生成的时间一致性难题与时空建模演进
基于bbox的PDF文档解析:多栏重排与水印过滤实战
探矿数据进RAG前的清洗全攻略:TXT、Word、PDF、网页四类文档处理实践

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

WuKongIM 分层架构重构指南:用 Access / Usecase / Runtime 三层替代 internal/service

发布时间:2026/10/15 3:40:12
WuKongIM 分层架构重构指南:用 Access / Usecase / Runtime 三层替代 internal/service 即时通讯后端【免费下载链接】WuKongIMMore than just IM 不只是即时通讯(IM)项目地址https://gitcode.com/gh_mirrors/wu/WuKongIM点击查看免费下载导读本文以仓库中的重构计划《Access / Usecase / Runtime Implementation Plan》为主体讲解 WuKongIM 如何将一度承担网关会话、帧路由、业务用例、在线会话运行时四种职责的internal/service巨型包拆解为internal/access入口适配层、internal/usecase应用用例层、internal/runtime节点本地运行时层与internal/app唯一组装根四个清晰分层。读完本文你将掌握这套分层架构的依赖规则、命令/结果模型设计、迁移落地步骤含 TDD 与提交点并能对照当前仓库源码验证每一层的实际实现形态为后续新增 HTTP API、RPC 等入口提供只加适配器、不动业务代码的扩展范式。一、重构背景internal/service为什么会成为耦合热点1.1 四个职责混在一个包里计划文档明确指出现状internal/service同时包含四类互不相同的关注点网关 handler 生命周期适配——会话打开/关闭如何落到本地在线会话管理帧路由与线上模型分支——wkpacket各帧类型如何分发消息用例逻辑——发送、确认等业务规则在线会话注册表与本地投递运行时——节点本地的连接索引与实时投递。这种结构在只有一个入口gateway时勉强可用但存在两个必然的演化结局新增 HTTP API、RPC、定时任务后更多入口特有代码继续堆进internal/service或者出现并行的api service、rpc service、task service包复制粘贴同一套用例逻辑。两种结局在结构上都不可持续。规格文档 2026-04-01-access-usecase-runtime-design.md 称之为 coupling hotspot耦合热点。1.2 重构的目标与非目标目标消除internal/service作为什么都能装的应用层入口特有关注点收敛进薄适配包业务用例可在gateway、api、rpc、task之间复用节点本地运行时原语与业务用例分离internal/app保持为唯一的组装根未来入口扩展是增量式的而不是结构重排式的。非目标刻意不做不对internal/service做公开兼容保留不引入domain/entity/repository/factory这类重型 DDD 仪式不一次性建设所有未来入口适配器access/api、access/rpc、access/task等空目录在真正有代码之前一律不建不在本次重构中解决完整远端投递语义。二、目标架构与各层职责2.1 目标目录结构规格文档给出的长期结构如下internal/ app/ app.go build.go lifecycle.go config.go access/ gateway/ handler.go frame_router.go mapper.go lifecycle.go api/ # 有真实代码前不创建 rpc/ # 有真实代码前不创建 task/ # 有真实代码前不创建 usecase/ message/ app.go command.go result.go send.go recvack.go deps.go session/ app.go bind.go unbind.go query.go deps.go user/ app.go deps.go channel/ app.go deps.go runtime/ online/ registry.go delivery.go types.go sequence/ allocator.go首个落地切片只要求四件事internal/access/gateway、internal/usecase/message、internal/runtime/online、internal/runtime/sequence其余未来目录严禁预创建空壳。2.2 各层职责边界internal/access入口适配层——只做适配接收入口特异的载荷与回调上下文把线上/传输模型翻译成用例命令调用用例 App把用例结果映射回入口特异的响应。它不负责可复用的业务规则、持久化业务状态、存储与集群依赖的组装方式。internal/usecase应用用例层——只做业务维护业务不变量权限、黑名单、封禁等编排存储、集群端口与运行时端口暴露与入口无关的命令/结果契约。它不负责理解gateway.Context、理解 HTTP 请求/响应对象、直接写协议帧。internal/runtime节点本地运行时层——只做运行时原语在线连接注册表本地实时投递本地序列号分配。它是进程运行时能力不是业务领域也不是入口适配器。internal/app进程组装层——只做组合构造运行时协作对象构造用例 App构造入口适配器把它们接线到 gateway、存储与集群组件。2.3 依赖方向与硬性规则计划文档规定的允许依赖方向access/* - usecase/*, runtime/* usecase/* - runtime/*, pkg/* runtime/* - internal/gateway/session (仅必要时), pkg/* app/* - access/*, usecase/*, runtime/*, pkg/*硬性规则对应实施计划的 Guardrails 一节usecase/*不得导入internal/gatewayusecase/*不得导入net/httpusecase/*不得以wkpacket线上对象作为命令主输入app/*保持唯一组装根不允许出现替代性的全局Service聚合对象。当前仓库的 internal/import_boundary_test.go 正是这类依赖边界的守护示例它用go/parser遍历包内全部.go文件的 import一旦发现导入被禁止的旧共享契约即t.Fatalf失败把依赖方向固化成可在 CI 中执行的测试。三、五个核心设计决策3.1 删除internal/service且不造新的胖聚合计划明确Do not create a replacement fatServiceobject under a different package name. 也就是说不能用换个包名的Service继续当万能桶。取而代之的是显式的用例 App例如规格文档中的示意type MessageApp struct { identities IdentityStore channels ChannelStore online OnlineRegistry delivery Delivery sequence SequenceAllocator clock Clock } type SessionApp struct { online OnlineRegistry clock Clock }3.2 把 gateway 当作适配器而非核心运行时gateway.Handler的实现应放在internal/access/gateway中职责被严格限定为三类翻译会话打开/关闭事件 → 会话用例调用入站wkpacket帧 → 消息命令消息结果 →wkpacket回复帧。3.3 重命名会话运行时概念剥离 gateway 词汇原SessionRegistry、SessionMeta、SessionsByUID等命名与网关词汇耦合过深统一更名为原名称新名称SessionRegistryOnlineRegistrySessionMetaOnlineConnSessionsByUIDConnectionsByUID这样运行时模型可被 API 侧在线查询与未来的推送流程直接复用。3.4 使用显式命令/结果模型入口适配器负责把线上对象翻译成用例命令例如type SendCommand struct { SenderUID string ChannelID string ChannelType uint8 Payload []byte ClientSeq uint32 ClientMsgNo string StreamNo string Topic string } type SendResult struct { MessageID int64 MessageSeq uint32 Reason uint8 }这条决策在仓库中已完全落地SendCommand与SendResult如今定义在 internal/contracts/channelappend/types.go由internal/usecase/message/types.go直接类型别名复用天然满足入口无关、跨 gateway/API 可复用。3.5 文件搬迁映射表计划文档给出的迁移映射也是后续演进可追溯的依据原路径新归属internal/service/handler.gointernal/access/gateway/handler.gointernal/service/frame_router.gointernal/access/gateway/frame_router.gointernal/service/send.gointernal/usecase/message/send.gointernal/access/gateway/mapper.gointernal/service/recvack.gointernal/usecase/message/recvack.gointernal/service/registry.gointernal/runtime/online/registry.gointernal/service/session_state.go拆分到internal/runtime/online/types.go与internal/access/gateway/lifecycle.gointernal/service/delivery.gointernal/runtime/online/delivery.gointernal/service/sequence.gointernal/runtime/sequence/allocator.gointernal/service/options.go拆分到各用例旁的deps.gointernal/service/service.go删除四、分阶段迁移实施路线TDD 独立提交点实施计划共分 5 个 Task每个 Task 遵循先写失败测试 → 跑测试确认 FAIL → 实现 → 跑测试确认 PASS → 独立 commit的节奏。Task 1提取节点本地运行时原语涉及文件新建internal/runtime/online/{types,registry,delivery}.go、internal/runtime/sequence/allocator.go及对应测试。Step 1 失败测试覆盖点在线连接的注册、覆盖写入、查找与注销按 UID 以稳定顺序列出连接某一个收件方写失败后本地投递对剩余收件方继续序列分配器对消息 ID 与各频道序列号独立递增。Step 2 / Step 4 验证命令go test ./internal/runtime/... -v预期包不存在时 FAIL实现后 PASS。Step 5 提交点git add internal/runtime/online internal/runtime/sequence git commit -m refactor(runtime): extract online and sequence primitives实施要点SessionMeta→OnlineConn、SessionRegistry→OnlineRegistry、SessionsByUID→ConnectionsByUID保持注册/注销、投递错误处理、内存序列分配的既有语义runtime 包内不得混入 gateway handler 逻辑。当前仓库印证internal/runtime/online已成为独立成型的包types.go 定义了OwnerRouteUID、HashSlot、OwnerNodeID、OwnerBootID、OwnerSeq、SessionID、DeviceID、DeviceFlag、DeviceLevel、Listener、ConnectedUnix、LastActivityUnix、LocalSession、RouteStatePending/Active/Closing与SessionHandle接口registry.go 实现了默认 32 分片defaultShardCount 32RegistryOptions.ShardCount 0时取默认值的线程安全注册表提供RegisterPending、MarkActive、MarkClosingAndUnregister、LocalSessionsByUID、RangeLocalSessions回调式遍历避免大注册表一次性分配、MarkTouched/DrainTouched/RequeueTouched有界 touch 批处理等 APIregistry_test.go 验证了OwnerRoute 不暴露 SessionHandle、按 UID 列出本地会话副本、快照统计各 RouteState 与 dirty touch 数等契约。包级流程说明见 internal/runtime/online/FLOW.md。关于序列分配计划中单独规划的internal/runtime/sequence/allocator.go在当前仓库中已不存在独立目录。从源码结构看消息 ID 与频道序列号的分配职责在后续演进中被并入了internal/runtime/channelappend的追加管线如 prepare.go 与 prepare_test.go 中ServerAllocatedMessageIDs的分配语义这与runtime 层只承载节点本地原语的定位一致分配原语随追加栈一起演化。Task 2构建与入口无关的消息用例涉及文件新建internal/usecase/message/{deps,command,result,app,send,recvack}.go及测试。Step 1 失败测试覆盖点未认证发送命令被拒绝不支持的 channel-type 处理本地单人会话发送成功无本地收件方路径返回当前的 not-on-node 原因投递失败产生当前的 system-error 原因。测试必须直接调用message.App.Send(...)不得 importinternal/gateway。Step 2 / Step 4 验证命令go test ./internal/usecase/message -vStep 5 提交点git add internal/usecase/message git commit -m refactor(message): introduce ingress-agnostic usecases实施要点主输入不接受*wkpacket.SendPacket改收SendCommand这类入口无关命令通过依赖接口identity 查找、channel 查找、在线注册表、投递、序列分配、时钟协作保持单人频道现有发送行为RecvAck即使首轮是 no-op 也要显式保留。当前仓库印证internal/usecase/message已发展为包含 40 文件的完整用例包。app.go 中的Options以依赖注入方式罗列了全部协作对象Submitter、Reader、PermissionStore、PermissionBatchStore、PersonDirectory、SendHook、BeforeSendWebhook、SystemUIDs、PermissionCacheTTL、Now等New(opts Options)负责默认值填充与权限缓存构建——这正是把internal/service/options.go拆分进各用例deps.go的落地形态。send.go 中App.Send的调用链清晰可见checkSendPermission权限判定→beforeSendHook发送钩子→ensurePersonDirectory单人频道目录保障→submitter.Send追加提交失败原因统一收敛为SendResult.ReasonSendBatch/SendBatchEach则支持批量发送并按输入对齐返回结果。types.go 将SendCommand、SendResult、Reason等全部定义为channelappend契约的类型别名从根上杜绝了用例层与网关线协议耦合。Task 3用薄网关适配器替换 Service涉及文件新建internal/access/gateway/{lifecycle,mapper,frame_router,handler}.go及测试。Step 1 失败测试覆盖点会话打开注册在线连接会话关闭注销连接SendPacket映射为SendCommand结果再映射回SendackPacketRecvackPacket路由进消息用例不支持的帧返回ErrUnsupportedFrame。Step 2 / Step 4 验证命令go test ./internal/access/gateway -vStep 5 提交点git add internal/access/gateway git commit -m refactor(gateway): replace service with adapter layer实施要点handler.go独占gateway.Handler实现lifecycle.go负责从gateway.Context提取认证派生的连接信息frame_router.go对wkpacket.Frame做类型 switchmapper.go在wkpacket对象与消息命令/结果之间翻译严禁internal/usecase/message反向 importinternal/gateway。当前仓库印证internal/access/gateway/handler.go 实现了coregateway.Handler文件尾部有var _ coregateway.Handler (*Handler)(nil)断言OnFrame对帧类型做 switchPingPacket触发 presence touch 并回PongPacket、SendPacket走handleSend、RecvackPacket走handleRecvack、EventPacket按事件类型分发到message_updates.enable能力协商或保留的终端围栏terminal fencedefault分支返回ErrUnsupportedFrame。mapper.go 的mapSendCommandWithPayload从会话值中提取FromUID、DeviceID、DeviceFlag、协议版本再逐字段拷贝ClientSeq、ClientMsgNo、ChannelID、ChannelType、Setting、Topic、Expire、Payload、NoPersist、SyncOnce、RedDot等writeSendack则把SendResult的MessageID/MessageSeq/Reason映射回SendackPacketReasonCode: mapReason(result.Reason)默认 SEND 请求预算为DefaultSendTimeout 5 * time.Second。会话生命周期方面OnSessionActivate调用 presence 用例Activate并把已知失败分类activation_route_not_ready、activation_not_leader、activation_stale_route、上下文取消/超时OnSessionClose同时做 presence 注销与投递闭环上报OnSessionActivateRollback处理 CONNACK 发布失败后的回滚。包级职责边界见 internal/access/gateway/FLOW.md。Task 4重接internal/app为显式协作组装涉及文件修改internal/app/build.go、internal/app/app.go、internal/app/integration_test.go、internal/app/lifecycle_test.go。Step 1 失败测试覆盖点app 显式构建 runtime 协作对象app 在不经internal/service的情况下构造消息用例与网关适配器既有的启动/关闭顺序保持不变。Step 2 / Step 4 验证命令go test ./internal/app -vStep 3 实施要点构建runtime/online注册表构建runtime/sequence分配器构建usecase/message.App构建access/gateway.Handler把网关适配器传给gateway.New从App中移除service *service.Service字段与访问器若仍需只读访问器直接显式暴露 message app 或 gateway 适配器不得重新引入通用 service 桶。Step 5 提交点git add internal/app git commit -m refactor(app): compose access usecase runtime layers当前仓库印证internal/app/wiring.go 就是组装根的现役实现ensureOnlineRegistry()中以online.NewRegistry(online.RegistryOptions{})构建在线注册表wiring.go#L289-L293wireGatewayHandler(ownerNodeID)中把a.messages转成accessgateway.MessageUsecase、把在线投递作为可选的runtimedelivery.FeedbackHandler端口注入并组装accessgateway.New(accessgateway.Options{...})wiring.go#L898-L921。同时 internal/app/app.go 通过ClusterRuntime、GatewayRuntime、APIRuntime等窄接口描述生命周期表面而不是暴露一个万能 service 对象——与计划以显式协作者替代伞形服务对象的验收标准完全一致。Task 5删除internal/service并迁移剩余测试删除清单internal/service/下的errors.go、options.go、session_state.go、registry.go、delivery.go、sequence.go、send.go、recvack.go、frame_router.go、handler.go、service.go及全部*_test.go与testkit/*。Step 1 测试迁移规则运行时向测试 →internal/runtime/...消息行为测试 →internal/usecase/message网关 handler 与 mapper 测试 →internal/access/gateway网关支撑的端到端覆盖保留但目标改为新适配器接线。Step 2 定向全量验证go test ./internal/runtime/... ./internal/usecase/message ./internal/access/gateway ./internal/app -vStep 3 / Step 4确认无 import 后删除整棵internal/service再跑全仓验证go test ./internal/... ./pkg/...Step 5 提交点git add internal/runtime internal/usecase internal/access internal/app git rm -r internal/service git commit -m refactor: replace internal service with layered architecture五、Guardrails重构过程的红线实施计划把以下约束列为不可逾越的红线原文 Guardrails 一节不得以另一个包名创建替代性的胖Service对象不得让usecase/message导入internal/gateway不得让用例以*wkpacket.SendPacket这类线上模型作为主 API不得创建空壳的未来包如access/api直到有真实代码必须在拓宽消息语义之前先保持既有行为不变。前三条直接对应依赖规则与命令/结果模型设计第四条防止目录规划替代代码落地的形式主义第五条则保证整个迁移过程中网关消息行为不回归——这也是每个 Task 都坚持先写失败测试、逐步提交的原因。六、完成标准与当前仓库对照实施计划定义的完成标准如下完成标准当前仓库对照internal/service不再存在✅ 全仓搜索无任何internal/service引用包已彻底移除gateway 特有知识收敛在internal/access/gateway✅ 会话映射、帧路由、SENDACK 错误分类、trace 阶段均在 internal/access/gateway 内消息用例可不导入 gateway 直接测试✅ internal/usecase/message/send_test.go 等测试直接调用message.App的批次发送 APIruntime 在线原语可复用且命名与 gateway 会话词汇解耦✅OnlineConn/OnlineRegistry/OwnerRoute命名与 internal/runtime/online/FLOW.md 的职责描述一致internal/app组装显式协作者而非伞形 service 对象✅ internal/app/wiring.go 逐项接线在线注册表、消息用例、网关适配器值得一提的是计划中预留的access/api、usecase/session、usecase/channel等未来目录也遵循了有真实代码才创建的原则当前 internal/access/api 已因 HTTP 管理面真实需求而存在而未被预创建的空壳目录则没有出现——这印证了该计划的可执行性与纪律性。七、总结WuKongIM 的 Access / Usecase / Runtime 三层重构是一次典型的为增长而重构把网关独占的巨石internal/service拆成职责单一、依赖方向明确的分层让业务用例脱离线协议、让运行时原语脱离网关词汇、让组装职责收敛到唯一的internal/app。对正在阅读本文的开发者而言这套计划的直接价值在于当你需要在 WuKongIM 中新增一个入口HTTP API、RPC、后台任务时正确的做法不是往某个 Service 里继续堆代码而是新增一个薄薄的internal/access/ingress适配器、复用internal/usecase/message的命令/结果契约——业务逻辑无需任何结构性改动。赞分享即时通讯后端【免费下载链接】WuKongIMMore than just IM 不只是即时通讯(IM)项目地址https://gitcode.com/gh_mirrors/wu/WuKongIM点击查看免费下载相关推荐Webiny Admin 架构模式实战指南UseCase/Repository/Gateway 分层与 MobX 呈现层开发Webiny Admin 架构模式实战指南UseCase/Repository/Gateway 分层与 MobX 呈现层开发 导读 本文面向 Webiny 的CMS后端前端云原生AI 应用ESPectre 固件架构的三层拆分Core、Runtime 与 Frontend 分层设计解读ESPectre 固件架构的三层拆分Core、Runtime 与 Frontend 分层设计解读 ESPectre 是一套面向 ESP32 的 Wi Fi C人工智能机器学习物联网嵌入式智能硬件边缘计算WuKongIM 协议对象拆分重构从 wkproto 单一包到 wkpacket / codec / jsonrpc 三层协议架构WuKongIM 协议对象拆分重构从 wkproto 单一包到 wkpacket / codec / jsonrpc 三层协议架构 导读 本文围绕 WuKon即时通讯后端上一篇MLflow AI Gateway 基准测试实战测量追踪服务器网关的代理开销与并发性能下一篇GrapesJS Trait Manager API 完全指南组件设置面板的配置、事件与自定义类型开发创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号