尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Rabel 源码探秘:CreateTopic 业务类与 Fanli 事件驱动的设计之道

Rabel 源码探秘:CreateTopic 业务类与 Fanli 事件驱动的设计之道 Rabel 源码探秘CreateTopic 业务类与 Fanli 事件驱动的设计之道【免费下载链接】rabelAn open-source web forum built on the Ruby on Rails framework.项目地址: https://gitcode.com/gh_mirrors/ra/rabelRabel 是一个基于 Ruby on Rails 框架的开源 Web 论坛系统代码干净、结构清晰非常适合想学习 Rails 工程化设计的开发者。本文将从Rabel 源码中的app/business目录出发拆解CreateTopic 业务类的写法并揭秘其背后的Fanli 事件驱动机制看看一个发帖动作是如何做到「业务瘦身、关注点分离」的。为什么 Rabel 值得读源码很多论坛项目的业务逻辑都堆在 Controller 里一个create方法动辄几十行校验、建模型、发通知、更新计数……代码越写越长越改越乱。Rabel 的做法完全不同它把「发帖」「回帖」「打卡」这些核心动作全部抽成独立的业务类Service Object统一放在 app/business/ 目录下文件职责create_topic.rb创建话题发帖create_comment.rb创建评论回帖destroy_comment.rb删除评论check_in.rb每日签到与连续签到统计notify_mentioned_users.rb 提及用户并发送通知touch_commentable.rb回帖后刷新话题的活跃时间与最后回复人这些业务类的共同点是都继承自Fanli::Base也就是标题里提到的事件驱动核心。主角登场CreateTopic 业务类源码解析 先来看本次的主角——create_topic.rbclass CreateTopic Fanli::Base def initialize(user, channel, params) user user channel channel params params end def perform topic channel.topics.new(params) topic.user user return topic if topic.save end end别看它只有十几行逻辑其实很完整initialize接收依赖把当前用户、所属频道、表单参数三个对象注入进来业务类不自己去查数据库依赖关系一目了然perform只做一件事把话题挂到频道下、绑定作者、保存并返回结果返回值即结果保存成功返回topic失败则返回nil调用方拿到结果就知道成败。这种「构造函数注入依赖 单一perform方法」的写法就是经典的服务对象模式Service Object也是 Rabel 源码中最值得新手模仿的设计之一。瘦控制器一行代码搞定发帖流程 ️业务类写好后Controller 就变得异常清爽。看 topics_controller.rb 里的create动作def create if CreateTopic.with(current_user, channel, topic_params) redirect_to t_path(topic.id) else render :new end end整个发帖流程被压缩成了一行调用CreateTopic.with(...)。这里的.with是Fanli::Base提供的类方法——它负责实例化业务类、执行perform、然后在事件总线上广播事件。成功就跳转到话题页失败就重新渲染表单页Controller 不再关心任何业务细节。再看 comments_controller.rb 的回帖动作同样是这个套路comment CreateComment.with(current_user, commentable, comment_params)一致的调用风格让整个项目的 Controller 保持了极高的可读性这就是「胖业务、瘦控制器」的实践范本。解密 Fanli 事件驱动机制 ⚙️关键问题来了Fanli::Base的.with到底做了什么它和事件驱动又有什么关系答案藏在 Gemfile 里引用的fanli gem0.4.0和 config/initializers/business.rb 中Fanli.on(:create_topic, NotifyMentionedUsers) Fanli.on(:create_comment, NotifyMentionedUsers, TouchCommentable) Fanli.on(:destroy_comment, TouchCommentable)这套 API 的意思非常直白当「创建话题」事件发生时自动执行NotifyMentionedUsers当「创建评论」事件发生时执行NotifyMentionedUsers和TouchCommentable。这就是事件驱动的精髓业务类只负责「把数据存进数据库」这一件事事件总线负责在合适时机广播:create_topic、:create_comment等事件监听器Listener按需订阅事件各自完成「发通知」「刷新时间戳」等副作用。于是「发帖」和「通知 用户」彻底解耦——以后想加「发帖后发邮件」「发帖后推送站内信」只需新增一个监听器并订阅事件完全不用改动 CreateTopic 本体。小提示Rabel 的 business.rb 里这些订阅目前被注释掉了但事件驱动的「骨架」已经完整保留读者可以自行打开开关感受效果。事件订阅实战提醒与回帖触达 1. 自动识别 提及的用户notify_mentioned_users.rb 里的on_create_topic方法会在发帖事件后自动扫描标题和正文用正则Notifiable::MENTION_REGEXP匹配所有昵称去掉发帖人自己避免「自己 自己」按昵称查到真实用户逐个写入Notification通知记录。回帖场景的on_create_comment更进一步既会通知楼主「有人回复了你」也会识别评论中的 提及并单独通知还细心地排除了楼主本人。一套代码两种通知全靠事件驱动。2. 回帖后自动刷新话题热度touch_commentable.rb 则负责「保持列表热度」新评论出现时更新话题的last_replied_by最后回复人和last_replied_at最后回复时间删除评论时则回退到上一条评论的信息甚至清空为初始值。这也解释了论坛首页为什么总能把「刚被回复的帖子」顶到最前面。不止发帖其他业务类同样精彩 事件驱动的写法在整个业务层一以贯之比如 check_in.rb 的每日签到逻辑通过CheckedInAtQuery查询今天是否已签到已签到则直接返回生成今天的签到记录并保存若昨天也签到了则把「连续签到天数」加一。可以看到连签到这种轻量功能也被封装成了独立业务类配合 checkins_controller.rb 里的CheckIn.with(current_user)一行调用整洁得让人舒适。从 Rabel 源码中学到的三条设计经验 ✨读完 CreateTopic 业务类与 Fanli 事件驱动可以提炼出三点对新手最有价值的设计经验把业务从 Controller 中搬出来每个核心动作对应一个类类名即业务名代码即文档用事件解耦副作用主流程只做「写数据」通知、计数、缓存刷新全部交给事件监听器新增需求零侵入统一入口约定initialize注入依赖、perform执行逻辑、.with统一调度全项目一套规范团队协作成本极低。如果你也想让自己的 Rails 项目从「堆代码」进化为「设计良好」不妨把 Rabel 的业务层当作一份活教材——先读懂 create_topic.rb 这十几行再顺着 business.rb 的事件订阅把整个业务层串起来你会发现好代码真的可以像读故事一样轻松。【免费下载链接】rabelAn open-source web forum built on the Ruby on Rails framework.项目地址: https://gitcode.com/gh_mirrors/ra/rabel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表