【问题标题】:Architectural question: In what assembly should I put which class, for a clean solution?架构问题:我应该在哪个程序集中放置哪个类,以获得干净的解决方案?
【发布时间】:2009-07-19 09:50:59
【问题描述】:

序言:

这是迄今为止我在这里留下的最长的帖子......但我认为在这种情况下它是必需的。

长期以来,我一直对这类事情有疑问:如何命名程序集,以及如何在其中划分类。

我想在这里举一个应用程序的例子,只用最少的类来演示我想要理解的内容。

想象一个应用程序

  • 接受客户端消息,将它们存储在数据库中,然后将它们出列到 MTA 服务器。
  • 它是一个 Web 应用程序,具有 ASP.NET 接口来编写消息和附加附件。
  • 还有一个 Silverlight 客户端,因此 web 应用公开了一个 ClientServices WCF ServiceContract,带有一个 OperationContract (SaveMessage)。
  • 还有一个 Windows 客户端...与 Silerlight 合同做同样的事情。

好的。这应该足以证明我的无知了。

以上将需要以下类:

  • 留言

  • 消息地址

  • MessageAddressType(带有 From、To 的枚举)

  • 消息地址集合

  • 邮件附件

  • 消息附件类型

  • MessageAttachmentCollection

  • 消息异常

  • MessageAddressFormatException

  • MessageExtensions(消息的静态扩展)

  • MessageAddressExtensions(MessageAddress 的静态扩展)

  • MessageAttachmentExtensions(MessageAttachment 的静态扩展)

Project.Contract.dll

我第一次尝试将上述内容组织到正确的程序集中,将观察到 Message、MessageAddress、MessageAttachment、其属性所需的枚举(MessageAddressType、MessageAttachmentType)和它们所需的集合(MessageAddressCollection、MessageAttachmentCollection),都是被标记为 [DataContract] 以便它们可以在 WCF 客户端和服务器之间进行序列化。 作为两者的共同点,我想我会将它们移到一个名为 Contract 的中立共享程序集中。

Project.Client.dll

我需要服务器 [ServiceContract] 的客户端代理,它引用 Contract.dll 中的类。

所以现在服务器(也引用 Project.Contract.dll)现在可以保存从 WCF 客户端接收到的序列化消息,并将它们保存到数据库中。

插件

接下来我会意识到我希望这些对象由 3rd 方插件(例如病毒检查器)在服务器端进行处理...

但插件应该对变量具有只读访问权限(仅),以便检查变量,并在看到不喜欢的内容时抛出错误。

所以我会考虑让 Message 继承自 IMessageReadOnly ...但是该接口放在哪里?

Project.Interfaces.dll

如果我把它放在一个名为 Project.Interfaces.dll 的程序集中,这将适用于那些可以在没有引用 Contracts.dll 的情况下引用它的插件......但是现在客户端必须同时引用 Contracts 程序集和接口...听起来不是一个好的方向...

重复对象

或者,我可以有两个 Messages 结构(并复制其他 MessageAttachment 等类)...一个用于从客户端到服务器的通信(在 Contracts.dll 中),然后使用第二个 ServerMessage/ServerMessageAddress /ServerMessageAddressCollection 在服务器端,它继承自 IMessageReadOnly,然后看起来我更接近我想要的。 对于重复的对象,插件的访问受到限制,而服务器 BL 等对其工作相关的类型具有完全访问权限,而客户端具有不同但相同的对象...... 事实上......我可能应该开始将它们视为不相同的,让我更清楚地知道这些对象只是为了与客户交谈,即合同/通信对象)......

网站用户界面

这会带来...嗯...如果有两个不同的消息,并且它们现在具有不同的属性...哪一个最适合用于支持 ASP.NET 表单? ServerMessage 对象似乎最快(类型之间没有映射)......但是所有逻辑都已经针对客户端消息对象(具有不同的属性和内部逻辑)制定出来。那么我是否会使用 ClientMessage,并将其映射到 Servermessage,以跨不同媒体保持各种 UI 逻辑相同?还是我应该更喜欢映射,只重写 UI 验证?

第三种情况呢,Silverlight...Contracts 程序集是一个完整框架程序集...Silverlight 无法引用(不同的框架/构建机制)...所以我在 Silverlight 上的程序集side 可能是完全相同的代码,但必须是不同的程序集。效果如何?

究竟什么是 DataContract?

最后……我发誓,我的大问题快要结束了……那些不明确是 DataContract 的讨厌的额外类呢?

例如,MessageAddress 是一个 DataContract。好的。它公开的枚举是其中的一部分...有道理...但是如果 messageAddress 构造函数引发 MessageAddressFormatException...它是否被视为 DataContract 的一部分?

可以有服务器、客户端和插件通用的类吗?

或者它是 ServerMessageAddress 和 ClientMessageAddress 共有的异常,所以不应该重复,而是在 Common 程序集中......所以最终,客户端必须绑定到 Contracts AND Common? (我们不是带着接口组件沿着这条小路走下去的吗?)

常见的基类/接口呢?

这些异常应该有共同的基类吗?例如...ClientMessageAddressException、ServerMessageAddressException、ServerMessageVirusException(来自插件)...我是否应该努力让它们——尽可能地——都从抽象 MessageException 派生...或者是否有时间继承/reusse只是不再是一个合适的奋斗目标?

非常感谢您阅读本文。

我是一名开发人员,在技术方面我可以应付得来……但是这些问题,我不得不布置程序集、架构,我自己,让我非常困惑……并且浪费了我很多时间,当我开车时,把东西从一个组件移到另一个组件,看看哪个最合适,同时我不确定我在做什么,并试图不得到循环引用。 .

所以 - 真的 - 感谢您的收听,我希望能够描述如何清晰地布置上述内容的人能够阅读这篇文章,并希望能表达我在未来项目中的思考方式。

【问题讨论】:

    标签: c# architecture s#arp-architecture


    【解决方案1】:

    在花了 10 分钟编辑问题以进行格式化后,我仍然会否决它。没有方式我会阅读所有这些。

    去拿一份


    Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (2nd Edition)

    【讨论】:

    • 感谢您的建议。从标题来看,我之前曾认为它更多地是关于大型框架而不是应用程序。我不会发现与运行应用程序、Main() 入口点、定义的 BL、DL、UI、层或提及客户端/服务器类和服务的关系不大。但如果你说它会......感谢这本书的提醒,我会去拿的。我会遵循任何可以在这方面做得更好的路线...... PS:是的......是一个很长的帖子。对此感到抱歉。
    • 确保您还查看了 Microsoft 模式和实践站点,地址为 msdn.microsoft.com/en-us/practices/default.aspx
    【解决方案2】:

    作为一名建筑师,我了解到,第一次就让事情变得绝对完美是不值得的,完美是主观的。重构,尤其是在程序集之间移动类,并没有太大的成本。在我看来,你已经在逻辑和正确地思考事情了。以下是我对您的一些问题的意见

    问:我应该为我的数据合同类设置只读合同吗?

    插件很可能根本不知道您的数据合同。病毒检查器可能采用字节数组,拼写检查器采用字符串和语言环境等。如果您正在为插件制作通用接口层,您应该只隔离与插件特定的数据共享的内容。这将允许您最大限度地重复使用它们。因此,我认为为数据合约结构创建接口不会得到什么回报,这些数据合约结构应该主要是一些愚蠢的数据包,几乎没有逻辑,实际上是接口本身。

    问:我应该在我的 ASP.NET 应用程序中使用与 Silverlight 应用程序相同的数据协定类还是直接使用服务器端类?

    我会使用客户端消息对象,这样您就可以从代码重用中受益。对象创建相当便宜,而且我确信大多数映射都是一对一的。它确实没有那么快,但这不会成为您的应用程序的瓶颈。

    问:我的异常类应该放在哪里?

    我会将您的示例异常类与数据合同一起放入程序集中,因为它们都是由于违反合同或作为在履行合同时传达错误的一种方式而引发的。

    问:异常应该有共同的基类吗?

    我还需要这样做,但我不像你那样了解你的代码库。我的猜测是,如果有什么好处的话,它对你的好处是微乎其微的。

    编辑:

    您可能对未来计划过度。根据我的经验,采用YAGNI 方法可以让我们更快地完成重要的事情。进行渐进式设计更改比花费宝贵的时间构建您可能永远无法从中受益的复杂架构更可取。

    【讨论】:

    • "我应该在网站中使用相同的数据契约...还是直接使用服务器端类?"出色的!因此(概括答案的一部分)而在仅 Web 的应用程序中很少出现问题,在 RIA 和客户端/服务器应用程序中,需要客户端对象,更好的方法是更改​​ Web UI 层以使用常见的客户端对象,并将它们映射到服务器对象:保持通用的验证逻辑。替代方案(例如:网站已启动 B4 Silverlight 出现)是保持网站使用相同的旧服务器对象,代价是重复 UI 逻辑和验证。对吗?
    • A:“异常应该在 DataContract 中”。概括:在需要的地方创建异常、枚举等。 (在本例中为 Client.DataContract)。将苹果与苹果等放在共同的命名空间/程序集中仅出于组织的目的,几乎没有/没有回报。不过,有一个验证:代码重用/通用接口是一个非常松散的目标?例如:如果我需要插件引发的类似异常(没有对客户端数据合同的引用),那么——即使代码重用/通用接口是有价值的目标——也必须开发一个重复的异常,甚至可能没有通用接口。跨度>
    • 问:我应该为我的数据合同类设置只读合同吗?同意你的回答。除了...如果使用从客户端对象映射到的服务器对象,这些 CAN 具有传递给插件的只读接口(具有有限数量的暴露成员)?或者更进一步,看到传递给插件的数据,即使在同一层,也与服务器对象完全分开......有点像插件合同/必须在插件边界映射的对象? (即插件“客户端”对象?)还有......引发的事件数据呢?服务器对象的只读接口?还是仅映射事件的对象?
    • Jacob:只是想说 HUUUUGE 感谢您到目前为止的回答。我想我一直在试图重用代码(并且只在一个地方调试,以降低成本)并使用通用接口,让编译器尽早发现违规行为......我最终陷入了困境蜡球,使我自己的代码变硬。似乎重复的代码,客户端和服务器对象之间的映射,以及更少的全局接口(服务器或客户端都可以,但不是两者都可以)......将成为当今的新秩序......这听起来像是一种有效的方式想想看?再次...非常感谢!
    • 评论 1 和 2:是的。评论 3:同意减少插件和事件使用的数据,以便它们特定于与消费者的合同。总体:给自己休息一下,不要花太多时间来规划理论上的未来需求。我认为 YAGNI 原则非常强大。 en.wikipedia.org/wiki/You_Ain't_Gonna_Need_It
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 2021-03-20
    • 1970-01-01
    • 2022-11-30
    相关资源
    最近更新 更多