11

使用scalasbt用于构建和git版本控制,当您的团队代码超出单个项目时,什么是组织团队代码的好方法?在某个时候,您开始考虑将代码分离到单独的库或项目中,并根据需要在它们之间导入。你会如何组织这些事情?或者你会避免诱惑,只管理同一个 sbt 和 git 单一“项目”下的所有包吗?

兴趣点是:(随意更改)

  • 避免发明过度设计想象需要的新“头痛”。
  • 在给定的开发机器CI server.
  • 用于生产的包装:能够用来SbtNativePackager包装您的东西以用于生产,而不会带来太多痛苦。
  • 轻松控制您在给定开发机器上使用的每个库的版本,并能够在它们之间无缝切换。
  • 避免 git 操作变得比通常情况下更糟。

此外,您会使用某种“本地 sbt/maven 团队存储库”吗?可能需要做些什么来完成它?希望这不是必需的。

谢谢!

4

2 回答 2

13

我在沙子中使用以下几行:

  • 最终进入不同可部署项目的代码进入同一个存储库的不同文件夹中,在一个伞式项目下——SBT 称之为多项目构建(我使用 maven 而不是 SBT,但概念非常相似)。它将被构建/部署到不同的 jars。

在进行有意义的划分时,我会尝试考虑最终的可部署项。例如,如果我的系统 foosys 有foosys-frontendfoosys-backend可部署的,foosys-frontendHTML 模板和foosys-backend与数据库的对话在哪里,并且两者通过 REST API 进行通信,那么我将把它们作为单独的项目,以及一个foosys-core用于公共代码的项目。foosys-core不允许依赖 html 模板库(因为foosys-backend不想要那个),也不允许依赖 ORM 库(因为foosys-frontend不想要那个)。但我不担心将与 REST 库一起使用的代码与“核心域对象”分开,因为两者都foosys-frontend使用 foosys-backendREST 代码。

现在假设我添加了一个新的foosys-reports可部署对象,它访问数据库来做一些报告。然后我可能会foosys-database根据 来创建一个项目来保存和foosys-core使用的共享代码。而且由于不使用 REST 库,我可能也应该从. 所以我最终得到了一个库,另外两个依赖于它的库项目(和),以及三个可部署的项目(依赖于、依赖于和依赖于两者)。foosys-backendfoosys-reportsfoosys-reportsfoosys-restfoosys-corefoosys-corefoosys-databasefoosys-restfoosys-reportsfoosys-databasefoosys-frontendfoosys-restfoosys-backend

您会注意到,这意味着对于可能使用该代码的每个可部署组件组合都有一个代码项目。进入所有三个可部署的代码进入foosys-core. 仅包含在一个可部署项目中的代码将进入该可部署项目的项目。进入三个可部署项目中的两个的代码进入foosys-restor foosys-database。如果我们想要一些代码是foosys-frontendfoosys-reports可部署的一部分,但不是foosys-backend可部署,我们必须为该代码创建另一个项目。从理论上讲,这意味着随着我们添加更多可部署项目,项目数量会呈指数级增长。在实践中,我发现这并没有太大问题 - 大多数理论上可能的组合实际上没有意义,所以只要我们只在实际有代码可以放入新项目时创建新项目就可以了。如果我们最终得到几个类foosys-core,它们实际上并没有在每个部署中使用,这不是世界末日。

在这种观点中,最好将测试理解为另一种可部署的。所以我会有一个单独的foosys-test项目,其中包含用于测试所有三个可部署项目(取决于foosys-core)的通用代码,也许还有一个foosys-database-test项目(取决于foosys-testfoosys-database)用于测试助手代码(例如数据库集成测试设置代码)foosys-backendfoosys-reports。最终,我们可能会得到一个完全平行的-test项目层次结构。

  • 仅当项目具有不同的发布生命周期时,才将项目移动到单独的 git 存储库(同时,单独的整体构建)。

不同存储库中的代码必须独立进行版本控制,因此在某种意义上这是一个空洞的定义。但我认为只有在必要时才应该继续使用单独的 git 存储库(类似于这篇文章:只有当您的数据太大而无法使用更友好的东西时,您才应该使用 Hadoop)。一旦您的代码位于多个 git 存储库中,您必须手动更新它们之间的依赖关系(在开发机器上,您可以使用 -SNAPSHOT 依赖关系和 IDE 支持来工作,就好像版本仍然同步一样,但您必须手动更新它每次与 master 重新同步时,都会增加开发摩擦)。由于您正在异步发布和更新依赖项,因此您必须采用并实施语义版本控制之类的东西,以便人们知道何时更新依赖项是安全的foocorp-utils而当它不是。您必须发布变更日志,并进行早期预警 CI 构建,以及更彻底的代码审查流程。这一切都是因为反馈周期要长得多;如果你在下游项目中破坏了某些东西,你不会知道这一点,直到他们在foocorp-utils几个月甚至几年后更新他们对 . . 因此,您需要流程来防止这种情况发生,而一切都会相应地变得不那么敏捷。

这样做的正当理由包括:

  • 您的项目的完整构建花费的时间太长,这会减慢您正在处理的代码的集成速度 - 尽管先尝试加快速度。
  • 部署所有可部署项目花费的时间太长——不过,再次尝试自动化并加快速度。保持一切同步有一个真正的优势,你不想放弃它,直到你绝对必须这样做。
  • 不同的团队需要处理代码。如果您彼此之间没有持续的沟通,那么无论如何您都需要进程开销(语义版本控制等),因此您最好获得更快的构建时间。(要明确一点,我认为每个 git 存储库都应该有一个单独的团队来拥有并负责它,并且当团队拆分时,他们应该拆分存储库。我对发布过程和职责有进一步的想法,但是这个答案已经很长了) .

我会使用一个团队 Maven 存储库,可能是Nexus。实际上,即使在您进入多项目阶段之前,我也会推荐这个。它非常容易运行(只是一个 Java 应用程序),您可以通过它代理您的外部依赖项,这意味着您有一个可靠的依赖项 jar 源,即使您的上游依赖项之一消失,您的构建也将是可重现的。

我打算将我的团队工作方式写成一篇博文,但与此同时,我很乐意回答任何进一步的问题。

于 2014-10-21T13:59:18.500 回答
4

我在这里有点晚了,但是我的 2 美分。

大多数 scala 项目和/或我在过去工作中从事的任何项目都以非常相似的结构告终。通常与其他团队成员达成共识(这有助于验证决定)。唯一的主要哲学差异是在技术基础设施层或业务模块上分离项目。以下示例:

常见项目

  • App.Utils:所有其他项目使用的共享实用程序代码(最少到 0 个依赖项)
  • App.Core:共享业务代码(模型、核心助手、接口、类型)

选项 1:模块分离

  • App.Inventory:带有服务、数据库代码、助手的库存模块
  • App.Orders : 带有服务、数据库、助手的订单管理模块

这可以非常方便且易于按业务区域进行管理,然后您可以根据需要部署单个模块。如果需要,您还可以稍后决定将模块分离为单独的 API(共享代码库仍在 utils 和 core 中)。这里的缺点是该方法会使项目数量膨胀。

选项 2:技术层分离

  • App.Database : 数据库访问函数
  • App.Services:业务服务的核心实现

在这种方法中,所有领域的所有逻辑/服务都在服务项目中,同样适用于数据库。所以说库存的代码在数据库和服务项目中分开。这允许按传统技术层进行分离。对于较小的项目,这可能要快得多。

就个人而言,我更喜欢选项 1 中更模块化的分离。它更具可扩展性,并且在进行代码更改时通常感觉更简单。

-K

于 2017-01-17T07:03:34.217 回答