如何在严格分层的架构中拆分层并促进模块化而不会造成不必要的冗余?

我已经收到了开始为我公司的代码库建立新架构的基础。 这项倡议的推动力是:

  • 我们的代码库已有十多年的历史,并且在我们尝试扩展时最终在接缝处打破。
  • 如果你想称它们为顶层的“层”,那就是经典的ASP和.NET。
  • 我们的数据库充满了一堆不圣洁的存储过程,其中包含数千行业务逻辑和validation。
  • 以前的开发人员创建了“聪明”的解决方案,这些解决方案是不可扩展的,不可重用的,并且具有非常明显的反模式; 这些需要在短时间内弃用。

当我致力于初始设计时,我一直非常重视MS模式和实践架构指南 ,但在我做任何事情之前,我仍然有一些挥之不去的问题。 在我开始讨论问题之前,这是我到目前为止的架构:

(高水平)

高水平

(业务和数据层深入)

业务和数据层深入

这些图基本上显示了我打算如何将每个层分成多个组件。 所以在这个候选架构中,我们有11个组件,不包括最顶层。

这是故障,每个组件的描述:

  • Company.Project.Common.OperationalManagement :包含实现exception处理策略,日志记录,性能计数器,配置和跟踪的组件。
  • Company.Project.Common.Security :包含执行身份validation,授权和validation的组件。
  • Company.Project.Common.Communication :包含可用于与其他服务和应用程序(基本上是一堆可重用的WCF客户端)通信的组件。
  • Company.Project.Business.Interfaces :包含用于从高级层与业务层交互的接口和抽象类。
  • Company.Project.Business.Workflows :包含与业务工作流的创建和维护相关的组件和逻辑。
  • Company.Project.Business.Components :包含封装业务规则和validation的组件。
  • Company.Project.Business.Entities :包含代表高级业务实体的数据对象。 其中一些可能是唯一的,一些可能是由来自数据层的更精细数据实体形成的复合物。
  • Company.Project.Data.Interfaces :包含用于与存储库样式中的数据访问层交互的接口和抽象类。
  • Company.Project.Data.ServiceGateways :包含用于调用和从外部系统获取数据的服务客户端和组件。
  • Company.Project.Data.Components :包含用于与数据库通信的组件。
  • Company.Project.Data.Entities :包含更多细粒度的实体,这些实体表示低级别的业务数据,适合以事务方式持久保存到数据库或其他数据源。

我的意图是这应该是一个严格的分层设计(一个层可能只与它下面的层进行通信),并且层的模块化分解应该促进高内聚和松散耦合。 但我仍有一些担忧。 以下是我的问题,我认为这些问题足够客观,因此它们适用于此…

  1. 我的每个模块及其各自的程序集的命名约定是否遵循标准约定,或者我应该采用不同的方式进行此操作?
  2. 将业务和数据层分成多个程序集是否有益?
  3. 在自己的程序集中为每个层设置接口和抽象类是否有益?
  4. 最重要的是 – 为业务层和数据层都有一个“实体”程序集是否有益? 我关注的是,如果在数据访问组件中包含将由LINQ to SQL生成的类,则给定实体将在代码库中的三个不同位置表示。 显然,像AutoMapper这样的工具可能会有所帮助,但我仍然不是100%。 我让它们像这样分开的原因是A – 强制执行严格的分层架构和B – 促进层之间更松散的耦合,并在每个实体背后的业务域发生变化时最大限度地减少破损。 但是,我想从那些比我更有经验的建筑师那里获得一些指导。

如果你能回答我的问题或指出我正确的方向,我将非常感激。 谢谢。


编辑:在阅读狒狒的答案之后,想要包括一些看起来更相关的其他细节。 数据库表也是一个不圣洁的混乱,充其量只是准关系。 但是,我不允许完全重新架构数据库并进行数据清理:我可以去的最核心的是创建新的存储过程并开始弃用旧的过程。 这就是为什么我倾向于在数据层中明确定义实体 – 尝试使用LINQ to SQL(或任何其他ORM)生成的类作为数据实体似乎不可行。

我实际上刚开始做同样的事情,所以希望这将有助于或至少产生更多评论,甚至帮助自己:)

1.我的每个模块及其各自的程序集的命名约定是否遵循标准约定,或者我应该采用不同的方式进行此操作?

根据MSDN Names of Namespaces ,这似乎没问题。 他们将其列为:

.(|)[.][.]
For example, Microsoft.WindowsMobile.DirectX.

2.将业务和数据层分成多个程序集是否有益?

我绝对认为将业务和数据层分解为多个程序集是有益的。 但是,在我的解决方案中,我只创建了两个程序集(DataLayer和BusinessLayer)。 其他细节,如InterfacesWorkflows等,我会为每个程序集下创建目录。 我不认为你需要在那个级别拆分它们。

3.在自己的程序集中为每个层设置接口和抽象类是否有益?

有点赞同以上评论。

4.为业务层和数据层构建“实体”程序集是否有益?

是。 我会说你的数据实体可能不会直接映射到你的商业模式。 将数据存储到数据库或其他介质时,您可能需要更改一些内容以使其发挥出色。 您向服务层公开的实体应该可用于UI。 您用于数据访问层的实体应该可用于您的存储介质。 AutoMapper绝对是您的朋友,可以提供您提到的映射帮助。 所以这就是它的形状:

服务层详细信息http://i.msdn.microsoft.com/dynimg/IC350997.png

我不同意这种标准的分层架构,而是支持洋葱架构 。

在此处输入图像描述

据此,我可以尝试一下你的问题:

1.我的每个模块及其各自的程序集的命名约定是否遵循标准约定,或者我应该采用不同的方式进行此操作?

是的,我同意这不是一个糟糕的惯例,而且非常标准。

2.将业务和数据层拆分为多个程序集是否有益?

是的,但我宁愿有一个名为Domain(通常是Core.Domain)的程序集和另一个名为Data(Core.Data)的程序集。 域程序集包含所有实体(根据域驱动设计)以及存储库接口,服务,工厂等…数据程序集引用域并使用ORM实现具体的存储库。

3.在自己的程序集中为每个层设置接口和抽象类是否有益?

取决于各种原因。 在上一个问题的答案中,我已经提到将存储库的接口分离到域中,以及数据汇编中的具体存储库。 这为您提供了清洁域,没有任何特定数据或任何其他技术的“污染”。 一般来说,我通过思考面向TDD的级别来构建我的代码,从类中提取所有依赖项,使它们更加可用,遵循SRP原则,并考虑当团队中的其他人使用该体系结构时会出现什么问题:)例如,分离到程序集的一大优点是,您可以控制引用并清楚地说明“域中没有数据访问代码!”。

4.为业务层和数据层都有“实体”程序集是否有益?

我不同意,并说不。 您应该拥有核心实体,并通过ORM将它们映射到数据库。 如果你有复杂的表示逻辑,你可以拥有像ViewModel这样的对象,它们基本上只是用适合在UI中表示的数据来实现的。 如果您有类似中间网络的东西,您也可以拥有特殊的DTO对象,以最大限度地减少网络呼叫。 但是,我认为拥有数据和单独的业务实体只会使问题复杂化。

还有一点要补充一点,如果你要开始一个新架构,并且你在谈论已经存在了10年的应用程序,你应该考虑更好的ORM工具来自LINQ-to-SQL,entity framework或NHibernate(我在我看来选择NHibernate)。

我还要补充说,回答尽可能多的问题,因为在一个应用程序架构中很难,所以请尝试单独发布问题,更具体地说。 对于架构的每个部分(UI,服务层,域,安全性和其他交叉问题),您可以进行多页讨论。 此外,请记住不要过度构建您的解决方案,并且需要更复杂的东西!

1)命名绝对正常,就像SwDevMan81所述。

2)当然,如果WCF在几年内过时,你只需要更改你的DAL。

3)经验法则是问自己这个简单的问题:“我能想到一个我会巧妙地利用这个问题的案例吗?”。
在谈论你的WCF合同时,是的,肯定会将它们放在一个单独的程序集中:它是良好的WCF设计的关键(我将详细介绍)。
在谈论AssemblyA中定义的接口并在AssemblyB中实现时,那些接口中描述的属性/方法在AssemblyC中使用,只要AssemblyB中定义的每个类都通过接口在C中使用,就可以了。 否则,你将不得不同时引用A和B:你输了。

4)我能想到实际移动相同外观对象3倍的唯一原因是糟糕的设计:数据库关系制作不当,因此你必须调整出来的对象才能使用。
如果重做体系结构,可以使用另一个程序集,几乎在每个项目中使用,称为“实体”,用于保存数据对象。 每个项目我都指WCF。

另外,我要补充一点,WCF服务应分为3个程序集:ServiceContracts,Service本身和我们讨论过的实体。 关于最后一点,我有一个很好的video,但它正在工作,我会把它连接到tomorow!

HTH,

BAB。

编辑: 这是video。