使用Packwerk增强Rails应用程序的模块化

2020-09-24 05:39:29

2020年9月30日,美国东部时间下午1点,加入我们的ShipIt!礼品:Shopify的Packwerk。了解更多关于我们最新的开源工具的信息,该工具用于在Rails应用程序中创建具有强制边界的包。请注册。Shopify核心代码库很大,很复杂,并且与日俱增。为了更好地理解这些复杂的系统,我们使用软件体系结构来创建结构边界。Ruby没有提供很多开箱即用的边界强制。Ruby on rails只提供了一个非常基本的分层结构,因此如果没有任何可靠的边界强制模式,很难扩展应用程序。相比之下,其他语言和框架具有用于垂直边界的内置机制,如Elixir的伞形项目。随着Shopify的发展,我们必须建立一种新的体系结构模式,以便单体内的大规模域可以通过定义良好的边界进行交互,进而提高开发人员的工作效率和幸福感。因此,我们创建了一个开源工具来构建一个包系统,该包系统可用于在大规模Rails应用程序中指导和加强边界。Packwerk是一个静态分析工具,用于加强Ruby文件组之间的界限,我们称之为包。代码中的高内聚性和低耦合性理想情况下,我们希望在感觉很小的代码库上工作。让大型代码库感觉很小的一种方法是让它具有高内聚性和低耦合性。凝聚力指的是模块或类中有多少元素属于一起。例如,功能内聚是指将代码分组到一个模块中,因为它们都对单个任务有贡献。相关的代码一起更改,因此应该放在一起。另一方面,耦合指的是模块或类之间的依赖级别。相互独立的元素在实现位置上也应该是独立的。当某个特定的代码域具有一长串不相关的域的依赖项时,就没有边界的分隔。这些边界是代码之间的障碍。代码边界的一个示例是拥有单独的存储库和服务。要使代码在这种情况下协同工作,必须进行网络调用。在我们的例子中,代码边界指的是同一代码库中不同的关注域,因此,我们希望在应用程序中强制实施两种类型的边界-依赖关系和隐私。一个类可以具有来自其他类的常量的依赖项列表。我们需要一组相关代码的有意且理想的小依赖项列表。类不应该依赖于不被视为其依赖项的其他类。当模块中存在私有常量的外部使用时,就违反了隐私边界。相反,外部引用应该是公共常量,其中建立了公共API。大型Rails应用程序的一个常见问题是,如果整体中没有代码边界,开发人员会发现更难在各自的区域中进行更改。您可能还记得做了一个简单的更改,它惊人地导致代码库的不同部分中不相关的测试中断,或者在代码库周围挖掘以找到包含2000多行代码的类或模块。如果没有任何已建立的代码边界,我们最终会得到反模式,如意大利面代码和知道太多的大型类。在没有任何已建立的代码边界的情况下,我们最终得到的是反模式(如意大利面代码)和知道太多的大型类。随着低内聚和高耦合的代码库的发展,开发、维护和理解变得更加困难。最终,很难实现新功能、扩展和增长。这让从事代码库工作的开发人员感到沮丧。开发人员在开发代码库时的幸福感和工作效率对Shopif.Rails很重要,Rails就像一个开放概念的Living Spaclet,它将大型Rails应用程序想象成一个没有围墙的房子内的生活空间。一个开放概念的生活空间就像一个没有建筑边界的代码库。为了将不同类型生活空间的关注点分开,你可以战略性地安排家具,以指明边界。这正是我们在2017年的组件化努力中所做的。我们将有意义的代码一起移动到我们称为组件的文件夹中。Shopify的每个组件文件夹代表商业领域,例如订单和结账。在我们的开放概念类比中,设想有一个没有墙壁的浴室-很清楚浴室应该在哪里,但我们希望它与其他有墙的生活空间分开。组件化的努力是迈向Shopify这个伟大的整体模块化的第一步,但是我们离模块化代码库还很远--我们需要围墙。仍在进行跨组件调用,并且跨域共享活动记录模型。没有围墙强加这些边界,只是一种很容易被破坏的商定的社会契约。边界执行解决方案我们研究的目标是找到边界执行的解决方案。我们都知道的红宝石和我