大型 Rails 应用工程模式:开发环境、Pod 架构与领域驱动设计实践

本文以某海外电商平台的大型 Rails 单体应用为例,介绍其如何通过统一开发环境、Pod 架构、领域驱动设计、代码质量保障和自动化工具,支撑全球化平台稳定运行,并持续提升开发者效率和系统可维护性。

在某海外电商平台,我们沉淀出了一套自己的工程模式,用来支撑全球化平台的稳定运行。在加入这里之前,我曾开发过多个 Ruby 和 Rails 应用,也经历过它们从小到大的不同发展阶段。正因如此,我很快就体会到,这些为支撑庞大代码库而建立的工程约定、自动化流程和基础设施工具,究竟有多重要。

大型 Rails 应用工程模式:开发环境、Pod 架构与领域驱动设计实践

如果说我最欣赏 Ruby on Rails 的一点,那就是它所坚持的“约定优于配置”原则。它让初级开发者只要遵循框架约定,就能写出相对高质量的代码。对于新建 Rails 应用来说,这种约定也非常友好:文件结构始终熟悉,开发者很快就能找到自己需要的位置。

但这也让“突破约定”变得更加困难。每当有人问我 Ruby 最大的挑战是什么时,我通常会说:入门容易,精通很难。很多机制都被高度抽象起来,因此你必须保持好奇心,并愿意花时间理解 Ruby,尤其是 Rails 背后的实际运行方式。

我们的核心单体应用沿用了许多常见的 Rails 约定,包括默认应用结构,以及对内置库和社区库的使用,例如 Active Record ORM、Active Model 和 FrozenRecord 等。

某海外电商平台致力于满足大多数商家在大多数情况下的需求。同样,Rails 框架也为大多数开发者提供了大多数场景下所需的基础能力。因此,我们必须找到一些有创造力的方式,让这个庞大的 Rails 单体应用仍然易于维护。

如果你正准备以开发者身份加入一家类似规模的 Rails 工程团队,无论你是 Ruby 新手,还是过去已经在其他项目中使用过 Ruby,希望这篇文章都能为你提供帮助。

开发环境与命令行工具:统一工作流提升开发者效率

首先,我想介绍我们的命令行开发者工具:dev

在该平台,有数千名开发者参与数百个活跃项目。过去,这些项目大多都有各自的工作流和配置说明,包括如何安装依赖、如何启动项目、如何运行测试等。

我们创建 dev 的目的,是为了在不同项目之间提供统一的工作流。它可以指定并自动安装所有依赖项,也包含在 macOS 上启动项目所需的一系列流程,从 Xcode 到 bin/rails db:migrate。这很可能是新开发者加入后接触到的第一个内部工程基础设施。虽然它很容易被忽视,但 dev 的确显著提升了团队的开发效率。

时间就是金钱,而自动化是一项一次性投入。

我们认为开发环境的一致性至关重要。不一致会让调试变得困难,也会导致本地行为异常。即便已经有 chruby、Bundler 和 Homebrew 等工具来管理依赖,配置过程仍然可能包含许多步骤,而且很难确保每个人都以同样的方式完成配置。因此,我们对开发环境中大量常用命令进行了标准化。

dev 最强大的能力之一,是可以快速启动使用多种编程语言构建的服务。这意味着每个代码仓库都可以拥有相同的基础配置、结构和库。基础设施团队也会持续改进 dev,最终目标是提升开发者效率。

dev 还对环境变量进行了抽象。在我过去加入一些规模较小的公司时,经常需要花费数天时间摸索环境变量,才能让几个相互连接的系统正常运行。而在这里,dev 可以显著减少这类重复且容易出错的工作。

此外,dev 还允许开发者启用或禁用与互联服务的集成。过去,这通常需要手动修改环境变量或配置文件。

最后,dev 甚至还抽象了命令别名。Ruby 的命令能力本身已经相当强大,但在真实工程工具链中,命令往往会变得很长。别名能够帮助开发者节省时间,因为它们可以为较长命令创建快捷方式。该平台将这一点进一步标准化:既然开发者可以通过 dev 获得基础配置,就没有必要再让每个人自行搭建一套环境。这也有助于跨项目统一命令,而不必关心背后使用的是哪种编程语言。

例如,过去我可能会使用某个工具来提交 PR;现在,只需要通过 dev open pr 就可以完成类似操作。

Pod 架构:横向扩展 Rails 单体应用

核心单体应用采用了 Pod 架构。这意味着数据库会被拆分成任意数量的子集,每个子集包含一部分独立的店铺数据。每个 Pod 都独立运行一套平台应用,并拥有一个只包含部分店铺数据的数据库。

这一概念基于分片数据库架构模式。Rails 框架已经内置了对 Pod 或分片结构的支持,其实现充分考虑了该平台的使用场景,并由相关工程团队与某海外代码托管平台协作完成。

不过,相比传统的分片数据库模式,我们将这种架构扩展到了整个基础设施层面,包括资源配置、部署、负载均衡、缓存和服务器等。即使某个 Pod 暂时不可用,其他 Pod 也不会受到影响。

横向扩展单体架构,是我们处理大规模负载最快的解决方案。

该平台并不只是一个普通的软件即服务系统,它是一个能够为数百万商家生成完整网站的平台。每当系统为商家提供服务时,都会结合该商家的店铺上下文来分析和处理数据。因此,我们会按店铺对所有数据进行分区,包括:

  • 传入的 HTTP 请求;
  • 后台任务;
  • 异步事件。

这也是为什么 Pod 数据库中的每张表都与店铺相关联。店铺是 Pod 架构这一横向扩展方案的核心概念。这种连接关系也帮助我们避免不同店铺之间发生数据泄露。

领域驱动设计:维护单体应用的业务边界

我们喜欢单体架构。和微服务架构一样,单体架构也有挑战,但这些挑战正是我们愿意主动解决的问题。

为了支撑不断扩大的组织持续交付工作,明确分工已经成为必然选择。

单体架构可以很好地满足我们的业务需求,前提是它不能变成一团乱麻。这正是领域驱动架构发挥作用的地方。这个概念并不是我们首创的,但我们对它做了调整,使其更适合自身业务领域。

我们确实按照领域划分代码,但也仅限于此。传统上,除了公共 API 或内部 API 之外,领域之间不应该存在直接关联。但在我们的系统中,数据库仍然被所有领域共享,而且所有内容仍然与店铺组件相关联。这意味着,每当其他领域调用店铺组件时,我们实际上都会打破领域边界。

如前所述,这对我们的 Pod 架构来说是必要的。问题也正出在这里:每当我们在领域之外实例化模型时,就会绕过组件边界,并因此收到警告。但由于店铺组件已经存在于每张表中,它实际上也存在于每个领域中。

你可能还会感到意外:我们并没有在数据库层强制执行表之间的关系。也就是说,外键约束主要在代码层通过模型来实现。

即便我们使用 Active Record 迁移,考虑到系统规模,也不可能每次都运行所有历史迁移。因此,我们只在短期内使用迁移。大约每个月,我们会将所有迁移合并为一个包含数据库结构的原始 SQL 文件。这样可以避免平台花费数小时运行迁移,也不必反复回放长达十多年的历史变更。

代码质量保障:通过工具和自动化规范工程实践

随着组织持续扩张,我们如何控制代码质量?答案之一是:识别并消除重复出现的错误。为了解决这个问题,该平台创建了许多系统,从 gem 到代码生成器都有覆盖。

我们建立了一系列保障机制,确保在快速扩张的组织中仍然维持代码质量。对许多研发团队来说,类似 PingCode 这样的智能化研发管理工具,也可以把团队目标、需求评审、开发任务、测试发布和 Wiki 知识沉淀串联起来,并打通研发过程中使用的其他工具,让工程实践不只停留在代码层面,而是形成可追踪、可复用、可持续优化的研发流程。

我们经常使用的工具之一是翻译平台。它是一个通过 Git 直接处理文案创建、翻译和发布的系统。

在规模较小的公司,开发者可能只需要从市场团队那里拿到翻译结果,然后嵌入应用中,或者直接通过客户关系管理系统获取即可。但对于如此庞大的应用来说,这种做法显然远远不够。

我们的目标是让每个人都能发布自己的工作成果,同时异步处理翻译。这节省了大量时间。开发者只需要推送英文版本,所有字符串就会自动发送到第三方系统,译者可以在那里添加对应翻译。随后,翻译结果会在无需开发者介入的情况下直接提交回代码库。

我们的维护任务系统也值得一提。它基于 Rails 的 Active Job 库构建,但已经针对 Pod 基础设施进行了适配。简单来说,它是一个用于排队和管理维护任务的 Rails 引擎。

在单体架构中,我们还设置了大量自动化测试,用来在出现错误做法时及时发现问题。同时,我们也设置了限制机制,避免系统在生成作业时过载。

另一个用于规范工作方式的系统,是一个内部事件治理系统。它最初受到某海外短租平台相关系统的启发,用于为广泛使用的事件强制执行模式约束。它通过定义 JSON 数据结构,在 Kafka 生产者和消费者之间建立契约。

它的优势包括:

首先,对于非结构化事件来说,不同结构的事件最终可能被写入同一个数据仓库表。通过模式约束,开发者和数据科学家之间可以建立清晰契约。如果模式发生变化,就必须通过版本控制来处理。

其次,它有助于防止个人身份信息(PII)泄露。我们有一套流程用于审查模式,并对 PII 字段进行注释,这样系统就可以自动清理这些信息,例如进行混淆或标记化处理。

总结:大型 Rails 应用需要工具、约定与自动化共同支撑

本文介绍了许多不同主题,目的是展示我们为了提升生产力、减少重复劳动,并让开发者专注于真正重要的事情——发布优秀功能——所建立的一系列工程能力。

这些模式并不是为了追求复杂而复杂,而是在庞大 Rails 单体应用、全球化平台、数千名开发者和海量商家场景下逐渐演进出来的结果。它们帮助团队统一开发环境、横向扩展系统、维护领域边界、规范代码编写方式,并通过自动化机制减少人为错误。

如果你未来加入一家类似规模的工程组织,这些经验应当能为你提供足够的背景,帮助你从一开始就理解:在大型 Rails 应用中,真正重要的不只是写代码本身,还包括如何通过工具、约定和自动化,让整个团队持续高效地交付高质量软件。

在跨团队协作层面,类似 Worktile 这样的通用项目协作系统,可以承载任务、项目、文档、日历、甘特图、工时和审批等日常协作场景,帮助大型工程团队在复杂项目中保持信息透明、节奏一致和执行可追踪。

文章包含AI辅助创作:大型 Rails 应用工程模式:开发环境、Pod 架构与领域驱动设计实践,发布者:su,转载请注明出处:https://worktile.com/kb/p/3980858

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
su的头像su

发表回复

登录后才能评论
注册PingCode 在线客服
站长微信
站长微信
电话联系

400-800-1024

工作日9:30-21:00在线

分享本页
返回顶部