我应该在组件中混合技术吗?

时间:2011-10-12 10:41:28

标签: .net assemblies domain-driven-design project-organization

我有一个中型项目,它实现了大约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
  • 我最终会转出数据访问和演示技术。
  • 在一天结束时,无论如何,主应用程序都将引用所有程序集。
  • 按概念分组时,测试将在何处进行?我不是必须将它们放在单独的程序集中,以便它们不附带程序吗?

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

3 个答案:

答案 0 :(得分:2)

TL;DR:您应该同时执行这两项操作,但不要仅仅为了它而将项目拆分为多个程序集。通过将程序集拆分为可重用组件,您最终将在适当的时候使用这两种方法的组合。


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

  1. 允许其他程序集/应用程序通过引用程序集来使用您的代码
  2. 通过拆分成许多小型装配来缩小大型装配的尺寸
  3. 如果你不需要这两个中的任何一个(并且你将来不会),那么请保持简单的生活,只需把所有东西都塞进一个组件中。


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

    Company.Project.Concept.dll
    

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

    请注意,当重新使用一个概念时,很少想要重用该概念的所有概念层(Domain / Presentation / WinForms)。通常,您的概念只包含1层(例如某种形式的处理),或者当重新使用该概念时,您只对1层或可能2层感兴趣。在这种情况下,如果您的“Concept”程序集还包含其他额外的逻辑(例如WinForms),那么您只需引用永远不会使用的额外代码。出于这个原因,如果您拥有它们,它的正常可以分离出概念图层,例如:

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

    即。在你给出的例子中,我主张如果你需要9个组件,而不是3:

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

    当然,将你的项目拆分成大量的装配是完全没有意义的,除非这些个别概念实际上会在别处使用,这让我回到了我的第一点 - 除非你真的需要,否则不要打扰拆分装配,或者换句话说将你的程序集拆分成有意义的可重用组件

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

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

    虽然我会错误地将组件太少而不是太多 - 但是将组件分成两部分比将两个组件组合成一个组件更容易。 除非您能找到令人信服的理由将装配/项目拆分为多个装配体,否则不要打扰。

答案 1 :(得分:1)

我通常选择在第一种方法中使用较重的倾斜混合(按层分组),但这并不意味着它是最好的。我的理由是,通常大多数操作,甚至是跨概念的操作,都会分享某种功能,这使我能够在所有不同的概念内部共享辅助类/结构/等。

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

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

关于切换数据访问和表示技术,我建议您采用dependency injection / inversion of control方法强制您抽象出那些功能(接口/抽象基类)可以随时换掉。

例如,我的数据访问层,我有两个接口,一个用于repository pattern(读者),另一个用于unit of work pattern(作者,它适应了,我并不总是有一个对象的集合,有时我的提交方法只采用一个实例,并且一次只允许对一个实例进行操作。

这些接口不涉及数据访问技术;底层商店可以是我关心的所有文本文件;它只是暴露了获取我想要的数据并将数据写回的方法。根据需要,我可以根据自己的需要从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放在最后也都正确排序,但将它放在我的一般虚荣命名空间限定符之后允许轻松阅读列表而不会混淆Testing最后弄乱我期望的内容看看我是否正在看它的对手(正在测试的东西)。

答案 2 :(得分:1)

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

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

  • 可重用性:分离可能在其他环境或项目中重复使用的组件

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

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

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

  • 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