Visual Studio解决方案:这些解决方案是否有太多的项目?

14

我目前所在公司支持4个相关的Windows应用程序。对于每个应用程序,我们都有一个解决方案,每个解决方案包含50到100个项目。其中大约有40或50个项目是在一个或多个解决方案之间共享的。

为了让您有一个大致的了解,较大的解决方案有近90个项目,其中20个是UI项目(主应用程序、自定义控件项目、UI助手项目),另外20个是业务层项目,可能还有30或40个数据访问层项目以及其他的Windows服务项目和专业项目。

我一直觉得每个解决方案中的项目太多了。当然,我们谈论的是大型应用程序,但我不明白为什么许多项目不能合并。在我的上一家公司,我们实际上只有一个业务层项目、一个数据访问层项目和显然为每个Windows或Web应用程序建立的一个项目。

以下是由于项目数量过多而遇到的一些问题:

  • 编译时间长。
  • 通常应该是私有的类需要设置为公共类才能被同一层中其他项目访问。
  • 我最近开始使用性能分析器Eqateq。试用版只允许对10个dll进行分析。我的情况显然使得使用该应用程序变得更加复杂。
  • 跟踪项目之间的引用关系相当复杂。我确信有许多未使用的项目之间的引用。

相比之下,我没有发现这种情况的任何优势。

那么,我的问题是:

  • 什么时候您会决定创建一个新项目而不是只是在当前项目中创建一个文件夹?
  • 是否有任何我忽略的优势?

更新:

请记住,我并不是提议将所有内容放在一个单一的项目中。最少应该有一个数据访问层项目、一个业务逻辑层项目以及每个UI应用程序都应该有一个项目。我意识到即使对于大多数应用程序,这也是不切实际的。

我想说的是,如何定义程序集的“单一职责”的最佳实践?这是一个非常主观的问题,如果过于严格,会导致每个类都有一个程序集。

更新2:

所有三个答案都提供了有价值的信息,但由于我只能选择一个,因此我选择 Doc 的答案,因为他评论了如何确定 DAL 项目的数量。 我还链接到其他相关信息的问题:这里这里。 我也建议阅读这篇文章


1
这听起来确实是很多项目,但也许应用程序足够复杂,需要那种复杂度水平。有什么阻止你只拥有一个主解决方案,简单地引用其他项目的已编译程序集呢? - Robert Harvey
也许这个问题应该发在程序员交流社区。 - AShelly
关于处理如此庞大的解决方案的编译时间的快速建议:请参考Scott Hanselman的文章,了解如何在Visual Studio中启用“并行构建”:http://www.hanselman.com/blog/HackParallelMSBuildsFromWithinTheVisualStudioIDE.aspx - RoelF
5个回答

8
我们主要谈论的是.NET项目,对吧?将您的应用程序拆分为不同的项目/程序集,可以迫使您避免这些项目之间的循环依赖关系,因为C#和VB.NET编译器禁止此类依赖。如果您将所有内容放入一个大型项目中,每个类都可以引用该项目中的每个其他类,从而使您可以创建真正的依赖关系噩梦。如果您要使用单元测试和团队开发,则在不同的隔离DLL上工作并对其进行单独测试,对团队成员来说通常更容易。
您问题中提到的“跟踪引用”部分:如果您将所有内容放入一个大型DLL中,则您的类之间具有与您拥有独立DLL时相同的引用,但您无法再控制它们。对于“私有”类(我认为您指的是“内部”),情况也是如此。
当然,我不能告诉你的是,如果您的90个项目是经过精心选择的工作单元。这是一个关于“单一责任”,正确抽象级别和产生的依赖关系的问题,只能由了解您的应用程序系统详细信息的人回答。

编辑:到目前为止,我已经阅读了一些有关通过按命名空间定义组件边界并将多个组件放入一个物理程序集/一个VS项目来缩短编译时间的文章(比如这篇)。为了使这种方法可行,可能需要使用类似NDepend或类似工具来确保组件之间没有循环依赖关系。对于大型项目,这可能是更好的选择。


是的,这些是.NET项目。正如我在问题中更新的那样,我不是在谈论创建一个单一项目,而是关于如何定义程序集的“单一职责”方面。我的意思是,我可以在设计中定义X dll的职责是“处理所有数据访问”,在这种情况下,我将为所有DAL拥有一个单一项目。但是,这个定义是否太宽泛了呢? - Mike
关于避免循环依赖,我认为这是一个很好的论点。然而,如果真的很重视这个问题,我觉得一个分析类级别代码耦合的工具会更加合适。毕竟,一个编译非常顺利的解决方案仍然可能在每个项目内部有一堆紧密耦合的类。 - Mike
例如,是否拥有一个DLL用于您的DAL是好还是坏,很大程度上取决于您的DAL的外观。如果您的DAL反映了关系型数据库,并且您的数据库中仅具有一个数据模型/架构,该模型/架构由每个应用程序使用,则一个DLL可能是可以的。如果您有多个模型彼此之间没有直接关系,可能位于不同的模式或数据库中,并且每个应用程序仅访问其中一个模型,将DAL分裂开可能是个好主意。 - Doc Brown

2
看起来这是典型的过早优化例子。您团队中的开发人员似乎由于历史架构原因创建了许多项目,您质疑这种方法是正确的。
我个人的意见是,从每个层面开始一个项目(如您所提到的),例如GUI、业务层、数据访问层等。仅在出现特定需求时才应创建解决方案的其他项目,而不是过早地创建。此时,您可以重构解决方案,将一个“文件夹”移动到自己的单独项目中。
这将确保: - 构建时间更快 - 您拥有项目所需的确切数量的项目 - 您已记录为何创建特定项目的理由(它满足了什么需求)
在解决方案中创建许多特定项目只是“过度工程化”。您不会过度设计代码并在不需要的地方创建类。当需要一个具有单一职责的类时,您将重构代码以包括它。解决方案和项目也是如此。

1
如果一个解决方案中的项目数量引起每天的烦恼(例如,由于解决方案加载和编译时间等原因),那么您确实在解决方案中有太多的项目。尽管如此,备选方案也可能有些让人烦恼,因此您必须找到自己的“最佳平衡点”。
对于至少一部分项目,参考预编译程序集而不是解决方案中的项目是一种减少项目数量而不影响有关功能隔离的任何预先存在决策的方法。它还可以具有其他好处,特别是对于更大的团队。例如,引用的程序集可以在经过更加严格的测试之后发布给其余团队,而不仅仅是在处理引用项目之一的开发人员检查源代码变更时。
虽然避免循环依赖是绝对可取的,但我曾经见过将程序集隔离采取到了一些相当荒谬的极端情况。有一些工具(例如:NDepend、FxCop)可以帮助检测不适当的依赖关系,当仅为避免不适当的依赖关系是隔离的唯一原因时,我通常倾向于使用这些工具而不是程序集隔离。

1

拥有许多项目并不一定是坏事。

它的一个重要优势是可以创建具有层次结构的模块。也就是说,您的GUI类可以引用较低层,但反之则不行。

如果您有一个较低层(例如业务逻辑)引用了一个较高层(GUI),那么您将会有一个循环依赖,Visual Studio 将拒绝构建您的解决方案。

如果您将所有项目混合到单个项目中,则不会发生这种情况。当然,如果您在代码上运行DSM软件,您肯定可以发现这一点,但我认为让编译器帮助您更加安全。

关于您提到的私有类变成公共类的问题,让我更多地考虑您的API。这些项目应该合并在一起,或者这些类应该提取到第三个项目中。在考虑这些问题时,我尝试思考层次结构,但并没有银弹。

对于仅属于项目的类,我通常在这些“内部”类的命名空间中添加“internal”(或“detail”)单词以便声明为公共。例如:你知道来自MyApplication.Model.Publisher的类不应该访问来自MyApplication.Model.Vendors.Internal的任何类。我在Eclipse API中看到过这种技术。

我知道C#有“internal”关键字,但有时人们会不加思考地将其声明为公共。将其放在名称中是更强大的方法,因为使该类成为public需要更多的工作。

其他处理巨大软件的方法是采用插件方式,这将缩短构建时间。但这总会有代价。你可以查看OSGI了解更多信息。


你提到API让我感觉我们可能正在接近某些东西。我认为,如果您有一个包含仅由同一层中的其他程序集使用的类的程序集,则这些程序集是可以合并的候选程序集 - 不一定是,但至少应该讨论一下。毕竟,您被迫创建了一个公共接口,现在所有层都可以使用它,即使不应该使用。 - Mike

0

我认为这里真正的问题是单一职责原则。我指的不是类设计,而是 .Net 项目的目的。

通常情况下,一个项目用于:
1. 构建一些代码以输出 DLL、EXE、网站、API 等。
2. 控制解决方案中的访问和代码结构。

根据我的经验,大多数项目都用于第二种情况。 在大型解决方案中,这会导致非常广度优先的结构,很快变得不直观,减慢了构建速度,减慢了 Visual Studio(你好 Resharper!),意味着大量的依赖关系从 bin 移动到 bin,这使得所有这些 DLL 变得毫无用处。

例如,如果 C# 访问修饰符可以在命名空间边界(而不是项目)上表示,那么我们就不需要这样做,可以拥有有意义且直观的命名空间层次结构,只有在实际需要生成 DLL 时才需要项目。当然,我还没有完全想清楚这个问题,但我感觉在解决方案中有这么多项目并不太对。

Java 如何处理这个问题?大型 Java 项目是否有很多循环依赖关系?

我的答案是尽可能少地使用项目。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接