我应该在程序集中混合技术吗?

发布于 2024-12-09 10:26:16 字数 1113 浏览 6 评论 0原文

我有一个中等规模的项目,它实现了大约 20 个左右的不同概念。一开始,我选择基于概念层来组织程序集,如下所示:

MyProject.Domain.dll (References System.Data.Linq, etc.)
  \ConceptA\
  \ConceptB\
  \ConceptC\
  \...\

MyProject.Presentation.dll
  \ConceptA\
  \ConceptB\
  \ConceptC\
  \...\

MyProject.WinForms.dll (References System.Windows.Forms, etc.)
  \ConceptA\
  \ConceptB\
  \ConceptC\
  \...\

MyProject.App.exe (References all the above)

我最近在一本 DDD 书中读到,我应该根据它所代表的领域概念而不是技术层对程序集进行分组,如下所示:

MyProject.ConceptA.dll (References System.Data.Linq, System.Windows.Forms, etc.)
  \Domain\
  \Presentation\
  \WinForms\

MyProject.ConceptB.dll
  \Domain\
  \Presentation\
  \WinForms\

MyProject.ConceptC.dll
  \Domain\
  \Presentation\
  \WinForms\

MyProject.App.exe (References all the above)

我没有足够的经验来长期判断这两种方法。我想在复杂性和灵活性之间取得最佳平衡。我有一些担忧让我感到矛盾:

  • 按概念分组可以更轻松地找到我的代码,因为它都在一个地方。
  • 按技术分组可确保我不会从域层调用 MessageBox.Show
  • 我最终将放弃数据访问和表示技术。
  • 最终,所有程序集都将被主应用程序引用。
  • 按概念分组时,测试将去往何处?我是否必须将它们放在单独的程序集中,这样它们就不会随程序一起提供?

根据您的经验,哪种方法是最好的方法?

I have a medium-sized project, which implements about 20 or so different concepts. At the beginning, I chose to organize my assemblies based on conceptual layers, like so:

MyProject.Domain.dll (References System.Data.Linq, etc.)
  \ConceptA\
  \ConceptB\
  \ConceptC\
  \...\

MyProject.Presentation.dll
  \ConceptA\
  \ConceptB\
  \ConceptC\
  \...\

MyProject.WinForms.dll (References System.Windows.Forms, etc.)
  \ConceptA\
  \ConceptB\
  \ConceptC\
  \...\

MyProject.App.exe (References all the above)

I've recently read in a DDD book that I should group my assemblies based on the domain concept it represents, rather than the technology layer, like so:

MyProject.ConceptA.dll (References System.Data.Linq, System.Windows.Forms, etc.)
  \Domain\
  \Presentation\
  \WinForms\

MyProject.ConceptB.dll
  \Domain\
  \Presentation\
  \WinForms\

MyProject.ConceptC.dll
  \Domain\
  \Presentation\
  \WinForms\

MyProject.App.exe (References all the above)

I don't have enough experience to judge the two approaches in the long term. I want to strike the best balance between complexity and flexibility. I have a few concerns that make me feel ambivalent:

  • Grouping by concept makes it easier to find my code, since it's all in one place.
  • Grouping by technology makes sure I don't call MessageBox.Show from my domain layer.
  • I will eventually switch out the data access and presentation technologies.
  • At the end of the day, all the assemblies will be referenced by the main application anyway.
  • When grouped by concept, where will the tests go? Won't I have to put them in separate assemblies so they don't ship with the program?

In your experience, which is the best approach?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

就是爱搞怪 2024-12-16 10:26:16

TL;DR:你应该两者都做,但不要分开您的项目只是为了它而分成多个程序集。通过将程序集拆分为可重用组件,您最终将在适当的情况下结合使用这两种方法。


首先,我想说,根据项目的大小,可能不需要将概念或层分离到单独的程序集中 - 将代码分离到单独的程序集中的优点是双重的:

  1. 允许其他程序集/应用程序通过引用程序集来利用您的代码
  2. 通过拆分为许多较小的程序集来减少大型程序集的大小

如果您不需要这两个程序集中的任何一个(并且您将来也不会),那么请让您的生活简单而简单一切都集成到一个组件中。


其次,将代码分离到单独的程序集中的主要原因是为了重用该代码 - 例如,如果您在 Windows 窗体应用程序中使用一段处理逻辑,请将其分离为单独的程序集允许您在控制台或 Web 应用程序中重复使用该逻辑。出于这个原因,我通常发现最好的方法是分离概念,例如:

Company.Project.Concept.dll

其中“概念”是您想要重用的东西,无论是一组常见的 Windows 控件还是一些数据访问逻辑。

请注意,当重用一个概念时,很少有人想要重用该概念的所有概念层(域/表示/WinForms)。通常,您的概念仅包含 1 层(例如某种形式的处理),或者当重新使用该概念时,您只对 1 层或可能 2 层感兴趣。在这种情况下,如果您的“概念”程序集还包含其他额外逻辑(例如 WinForms),您只是引用了永远不会使用的额外代码。出于这个原因,如果你有概念层的话,它通常会被分成概念层,例如:

Company.Project.Concept.Processing.dll
Company.Project.Concept.WinForms.dll

MyProject.ConceptA.Domain.dll
MyProject.ConceptA.Presentation.dll
MyProject.ConceptA.WinForms.dll

即在你给出的例子中,我主张如果有的话,你会想要 9 个程序集,而不是 3个 当然,将您的项目拆分为数百个程序集是完全没有意义的,除非这些单独的概念实际上将在其他地方使用,这让我回到我的第一点 - 不要费心拆分程序集,除非您确实需要,或者将其放在另一个地方方式分割你的将程序集分解为有意义的可重用组件

  • 如果没有其他人打算使用您的 Windows 窗体控件,则不必费心将它们拆分为单独的程序集
  • 如果大多数人打算一起使用 ConceptB 和 ConceptA然后将它们组合成一个程序集
  • 如果大多数人想要一起使用域层和表示层,请将它们组合成一个程序集

作为一个工作示例,我自动选择将较小的项目拆分为两个程序集 - 应用程序本身包含所有内容“演示文稿”(无论是 Web、WinForms、 WPF 或控制台应用程序),以及另一个包含应用程序“核心”的程序集 - 应用程序公开的底层功能(例如图像处理、数据检索等)。如果我想在不同风格的应用程序中公开相同的功能,这会很有帮助。

不过,我还是会犯错误,即程序集太少而不是太多——将一个程序集一分为二比将两个程序集重新组合成一个更容易。 除非您能找到令人信服的理由将一个程序集/项目拆分为多个程序集,否则不要打扰。

TL;DR: You should be doing both, but don't split your project up into multiple assemblies just for the sake of it. By splitting your assemblies down to reusable components you will end up using a combination of both methods where appropriate.


Firstly, I'd say that depending on the size of your project there may not be any need to separate either concepts or layers our into separate assemblies - the advantages of separating out your code into separate assemblies is twofold:

  1. Allowing other assemblies / applications to make use of your code by referencing your assembly
  2. Reducing the size of large assemblies by splitting into many smaller assemblies

If you don't have any need for either of those two (and you wont in the future) then keep your life simple and just bung everything into one assembly.


Secondly, the primary reason for separating code out into separate assemblies is to re-use that code - for example if you have a piece of processing logic being used in a Windows Forms application, separating that out into a separate assembly lets you re-use that logic in say a console or web application. For this reason I usually find that the best way is to separate on concepts, for example:

Company.Project.Concept.dll

Where "Concept" is something that you want to re-use, be it a set of common Windows controls or some data access logic.

Note that when re-using a concept its fairly rare to want to re-use all conceptual layers of that concept (Domain / Presentation / WinForms). Normally either your concept only consists of 1 layer (e.g. some form of processing), or when re-using that concept you are only interested in 1 or possibly 2 layers. In this case if your "Concept" assemblies also contain other extra logic (such as WinForms) you are just referencing extra code that will never be used. For this reason its also normal to separate out into conceptual layers if you have them, for example:

Company.Project.Concept.Processing.dll
Company.Project.Concept.WinForms.dll

I.e. in the example you gave I'm advocating that if anything you would want 9 assemblies, not 3:

MyProject.ConceptA.Domain.dll
MyProject.ConceptA.Presentation.dll
MyProject.ConceptA.WinForms.dll

Of course splitting your project down into hundereds of assemblies is completely pointless unless these individual concepts are actually going to be used elsewhere which brings me back to my first point - don't bother splitting up assemblies unless you actually need to, or to put it another way split your assemblies up into the re-usable components that make sense:

  • If nobody else is going to use your Windows Forms controls then don't bother to split them into a separate assembly
  • If most people are going to use ConceptB and ConceptA together then combine them into a single assembly
  • If most people are going to want to use the Domain and Presentation layers together, combine them into a single assembly

As a working example I automatically opt to split smaller projects into two assemblies - the application itself which contains all the "presentation" (be it Web, WinForms, WPF or a Console app), and another assembly which contains the "meat" of the application - the underlying functionality being exposed by your application (e.g. image processing, data retrieval etc...). This helps if I ever want to expose the same functionality in a different style of application.

Once more though I would err on the side of too few assemblies rather than too many - its easier to split an assembly in two than it is to combine two assemblies back into one. Unless you can find a compelling reason to split an assembly / project up into multiple assemblies, don't bother.

梦旅人picnic 2024-12-16 10:26:16

我通常选择更倾向于第一种方法(按层分组)的混合,但这并不意味着它是最好的。我的推理是,通常大多数操作,甚至跨概念,都会共享某种功能,这允许我拥有帮助器类/结构/等,以便在所有不同概念之间进行内部共享。

当然,这也可以以其他方式应用,如果所有程序集都按概念分组,那么您可以拥有特定于概念而不是层的类/结构/等。

最后,这是关于你认为什么会获得最多的重用。在我当前的项目中,我将所有内容按层分割,但每个层都有一个特定的任务(有发现者、丰富者、阶段者),每个层都有单独的域(将这些任务应用到不同的网站)和层(一些在领域的本机表示,然后是数据层,等等)。

关于切换数据访问和呈现技术,我建议您采用依赖注入/< href="http://en.wikipedia.org/wiki/Inversion_of_control" rel="nofollow">控制反转方法迫使您抽象那些您需要的功能(接口/抽象基类)可以随时更换。

例如,我的数据访问层,我有两个接口,一个用于存储库模式(读者)和一个用于工作单元模式(作者,它经过改编,我并不总是有一组对象,有时我的提交方法采用单个实例,并且一次只允许对一个实例进行操作)。

这些接口不公开任何有关数据访问技术的信息;底层存储可以是我关心的所有文本文件;它只是公开了获取我想要的数据并将该数据写回的方法。我可以从 ADO.NET 切换到 LINQ-to-SQLLINQ-to-Entities(所有这些我都拥有 完成)按照我的意愿,根据需要。

至于测试,如果我有一个程序集,我会在与程序集相同的级别上进行测试:

Casper.Discovery.dll

下面有以下域和任务:

Casper.Discovery.dll
    DomainA
        Models
        Data
    DomainB
        Models
        Data

然后我将有一个测试程序集:

 Casper.Testing.Discovery.dll

下面的测试具有相同的目录/文件结构。我专门这样做,以便可以将测试程序集分组到标有“测试”的解决方案文件夹中。虽然即使 Testing 放在最后,它们的顺序也正确,但将其放在我的通用虚荣名称空间限定符之后可以轻松地细读列表,而不会混淆 Testing 在如果我正在查看它的对应项(正在测试的东西),最终会弄乱我期望看到的东西。

I usually opt for a mix with a heavier lean towards the first approach (grouping by layer), but that doesn't mean that it's the best. My reasoning is that usually most of the operations, even across concepts, are going to share functionality of some kind, and this allows me to have helper classes/structures/etc to share internally across all of the different concepts.

Of course, that can be applied the other way as well, if all of your assemblies are grouped by concept then you can have classes/structures/etc specific to the concept as opposed to the layer.

In the end, it's about what do you feel will gain the most reuse. In my current project, I have everything split by layer, but each layer has a specific task (there are discoverers, enrichers, stagers) that each have separate domains (different websites to apply these tasks to) and layers (some fetch data in the representation native to the domain, then there's the data layer, etc).

In regards to switching out the data access and presentation technologies, I suggest you adopt a dependency injection/inversion of control approach which forces you to abstract those pieces of functionality (interfaces/abstract base classes) which you can swap out whenever you want.

For example, my data access layer, I have two interfaces, one for the repository pattern (the reader) and one for the unit of work pattern (the writer, and it's adapted, I don't always have a collection of objects, sometimes my commit method takes a single instance and only allows operations on one instance at a time).

These interfaces expose nothing about data access technologies; the underlying store can be a text file for all I care; it just exposes the means to get the data that I want and write that data back. I can switch from ADO.NET to LINQ-to-SQL to LINQ-to-Entities (all of which I have done) at my desire, depending on the need.

As for testing, I test on the same level as my assemblies, if I have an assembly:

Casper.Discovery.dll

With the following domains and tasks underneath:

Casper.Discovery.dll
    DomainA
        Models
        Data
    DomainB
        Models
        Data

Then I'll have a testing assembly:

 Casper.Testing.Discovery.dll

With the same directory/file structure for tests underneath. I do this specifically so I can group the testing assemblies into a solution folder labeled "testing". While they all order correctly even if Testing was placed at the end, putting it after my general vanity namespace qualifier allows for easy perusal of the list without being confused about Testing at the end messing up what I expect to see if I was looking at its counterpart (the thing being tested).

柳若烟 2024-12-16 10:26:16

它不仅仅是按概念层分组,也不是按领域概念分组,首先考虑为什么要分组或分离程序集:

  • 可维护性:为了分离关注点并逻辑地分组相关概念,您可以按领域、数据方面进行分组访问、基础设施、分布式服务,在层抽象方面保持关注点分离。可维护性的另一个方面是分离有界上下文(业务概念),如果您的领域逻辑庞大且复杂,因此您可能通过有界上下文进行嵌套分组。

  • 可重用性:分离可能在其他上下文或项目中重用的组件

  • 动态替换组件:分离可能在运行时/部署时替换的组件,例如日志框架、数据访问层……

最终结果是,当您开发中型到大型复杂域解决方案时,您通常需要进行嵌套(多层)分组:

示例是按有界上下文进行分组,然后按层抽象进行分组:

  • 公司.CRM.Domain
  • Company.CRM.Repositories.NHibernate
  • Company.CRM.Repositories.EF
  • Company.CRM.Application
  • Company.Trading.Domain
  • Company.Trading.Repositories.NHibernate
  • Company.Trading.Application
  • 公司.共享.
  • 基础设施 公司.共享.基础
  • 设施.Log4NetLogger公司.共享.基础设施.EnterpriseLibraryLogger

It is not about grouping by conceptual layers only nor by domain concepts, think first of why do you group or separate assemblies:

  • Maintainability: To separate the concerns and logically group related concepts, you group by aspect in terms of Domain, Data Access, Infrastructure, Distributed Services, maintaining a separation of concerns in terms of layer abstraction. another aspect of maintainability is to separate bounded contexts (business concepts) if your domain logic is large and complex thus you may have nested grouping by bounded context.

  • Re-usability: To separate the components that likely to be re-used in other contexts or projects

  • Dynamic replace of components: To separate the components that may be replaced at runtime/deploy time, like the logging framework, the data access layer, ...

the end result is that you mostly will need to have a nested (multi level) of grouping when you are developing a medium to large complex domain solution:

example is to group by bounded context then by layer abstraction:

  • Company.CRM.Domain
  • Company.CRM.Repositories.NHibernate
  • Company.CRM.Repositories.EF
  • Company.CRM.Application
  • Company.Trading.Domain
  • Company.Trading.Repositories.NHibernate
  • Company.Trading.Application
  • Company.Shared.Infrastructure
  • Company.Shared.Infrastructure.Log4NetLogger
  • Company.Shared.Infrastructure.EnterpriseLibraryLogger
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文