.NET应用程序架构 - 这个类的最佳组件是什么?

时间:2011-01-25 09:19:16

标签: .net oop architecture n-tier-architecture

我有一个.net网络应用程序,我已经接管了,过去曾遭遇所有业务逻辑在页面后面的代码中,并且非常依赖于UI。

我花了一些时间进行重构,特别是将数据访问代码移动到专用数据访问项目“Company.DataAccess”(例如)。代码的其他逻辑部分也有自己的程序集。

我不满意的是需要在程序集之间共享的对象的放置:

例如 - 在我的项目“Company.ClientDataImport”中,我有包含用于导入客户端数据的业务逻辑的类。

包含此代码的一个特定功能是客户端导入格式可以映射到我们的默认导入格式。所以,我在“Company.ClientDataImport”程序集中有一个“DataImportFieldMappingInfo”类:

public class DataImportFieldMappingInfo {

    int CompanyFieldName
    {
        get;
        set;
    }

    int ClientFieldName
    {
        get;
        set;
    }
}

这些映射存储在数据库中,因此在某些时候,我需要使用此对象的实例填充集合。

一个架构目标是,所有数据库IO都应由“Company.DataAccess”处理。

我希望“Company.DataAccess”知道DataImportFieldMappingInfo类,以便它可以利用该类来存储此类数据,但这意味着“Company.DataAccess”和“Company.ClientDataImport”都是需要注意DataImportFieldMappingInfo,以便它们可以使用相同的类进行通信。

到目前为止,我对这个常见问题的解决方案是使用另一个名为“Company.DomainEntities”的库,该库包含在应用程序中的其他程序集之间共享的对象的类。这很有效,并且所有程序集都可以根据相同的对象进行通信。这些类实际上只包含属性,因此它们只是数据容器而不是包含应用程序逻辑。

我不喜欢这个是DataImportFieldMappingInfo是一个数据导入问题,因此我认为它应该在该命名空间中,因此应该在“Company.ClientDataImport”项目中。但是,循环引用限制会阻止这种情况,所以我必须使用“Company.DomainEntities”项目作为中间人。

我的架构有问题,还是在这种情况下常见的做法?

2 个答案:

答案 0 :(得分:2)

在这种情况下,将共享类型/接口分解为单独的程序集是相当正常的。

另一种方法,并非闻所未闻,只是简单地说“所有这些程序集都没有做任何额外的事情”,只需使用名称空间来划分代码。如果你不需要只能运行dll的子集,为什么不只有一个 dll?与此相反的经典对比是,您有多个不同的UI /服务都使用相同的dll(或dll的不同子集)。

答案 1 :(得分:2)

我倾向于同意马克;但是你可能会考虑其他一些事情:

什么构成“业务逻辑”与“数据访问”代码与您应用“知道”的简单数据结构?乍一看,DataImportFieldMappingInfo看起来可能与数据访问相关,但我认为它们实际上只是常见的结构(并且更加面向业务),因此将它们放在一个通用的程序集中会有意义 - 假设您使用它们在不同的层之间交换数据/ assembilies。

另一个观点是所有数据存储库(包括特定于您的应用程序的数据库以及所有外部数据源,如外部数据库,文件或系统)被平等对待并通过interface - 不是通过单一的具体实施。在这种情况下,所有数据都以业务逻辑/域中心方式指定,而不是以数据访问/存储库特定方式指定。在这种情况下,您还要定义应用程序在公共程序集中使用的所有常见数据结构。

修改:

简而言之,接口允许您抽象出与讨厌的东西相关的复杂性和依赖性,而这些东西你不是那么紧密(和永久)相关联的。

使用接口的原则是Dependency Inversion(DI)。 (关于这一点的信息并不缺乏 - 事实上你可能会发现太多了很多)。 DI一直致力于帮助构建与Stable Dependencies Principle配合良好的系统。

我们的想法是,不要让您的业务逻辑(程序集)依赖于数据访问,而是让它们都依赖于接口;然后,您可以随时更换实施。您可能有一个“真正的”数据访问组件可以对抗SQL,而“虚拟”组件可以直接从您的代码返回虚拟测试数据。

当您设计界面时,我们的想法是从业务逻辑/域角度设计它 - 而不是技术角度;虽然有些情况是合适的。实现的工作(即:实现接口的数据访问类)是在面向业务的接口和它正在处理的任何奇怪的数据源之间进行转换。例如,您的普通基于SQL的数据提供程序可能会通过DataReader吸取数据并转换为MyBusiness.Common.Info.OutstandingAccountsCollection

代码结构

  • Common Assembly(MyBusiness.Common) - 包含用于在各层之间交换数据的任何数据结构(例如:MyBusiness.Common.Info.OutstandingAccountMyBusiness.Common.Info.OutstandingAccountCollection类)。
  • 接口程序集:您可能有其中的几个。如果您通过一个系统(比如SQL)访问所有数据,那么您可能只有一个(MyBusiness.Interfaces.DataAccessProvider),但是如果您有各种系统要与您进行交互,那么您希望将它们分开。 MyBusiness.Interfaces.DataAccessProvider引用Common程序集,因为它将在其合同中包含'info'类型。
  • 具体数据访问程序集:(MyBusiness.DataAccess.SQLDataAccessProvider)。这引用了Common和Interface程序集。
  • 您的业务逻辑(MyBusiness.BusinessLogic)引用公共和接口程序集。

有关更多信息(和图表),请查看此部分(自我推广提醒):5-Layer Architecture