我正在使用 .NET 4 为客户创建一个小型客户端服务器应用程序。我应该创建一个实现许多合同(IInvoice、IPurchase、ISalesOrder 等)的巨型服务,还是应该创建许多服务,每个服务在多个端口上运行一个合同?我的问题特别对任何一种选择的利弊感兴趣。另外,回答这个问题的常用方法是什么?
我真正的困境是我没有做出这个决定的经验,而且我对 wcf 的经验也很少,我需要帮助来理解这种决定的技术含义。
我正在使用 .NET 4 为客户创建一个小型客户端服务器应用程序。我应该创建一个实现许多合同(IInvoice、IPurchase、ISalesOrder 等)的巨型服务,还是应该创建许多服务,每个服务在多个端口上运行一个合同?我的问题特别对任何一种选择的利弊感兴趣。另外,回答这个问题的常用方法是什么?
我真正的困境是我没有做出这个决定的经验,而且我对 wcf 的经验也很少,我需要帮助来理解这种决定的技术含义。
不要创建一个实现 n 个服务合同的大型服务。这些类型的服务很容易创建,但最终会成为维护方面的难题,并且无法很好地扩展。另外,如果有一个开发组竞争签入/签出,您将遇到各种代码合并冲突。
也不要创建太多服务。避免使您的服务过于细化的陷阱。尝试基于功能创建服务。这些服务公开的方法也不应该是细粒度的。你最好用更少的方法做更多的事情。通过创建 GetUser(userObject user) 避免创建类似的函数,如 GetUserByID(int ID)、GetUserByName(string Name)。您将拥有更少的代码、更容易的维护和更好的可发现性。
最后,无论您做什么,您都可能只需要一个端口。
更新 12/2018 有趣的是,自从我写这篇文章以来事情发生了怎样的变化。现在有了微服务模式,我正在使用聊天 API 创建很多服务 :)
在实时应用程序中,每个实体都有一份服务合同,例如 Invoice、Purchase 和 SalesOrder 将有单独的 ServiceContract
但是,对于每个服务合同,都会有异构客户端,例如 Invoice 将由后台通过 Windows 应用程序使用 netNamedPipeBinding 或 netTcpBinding 调用,同时客户端应用程序需要使用 basicHttpBinding 或 wsHttpBindings 调用服务。基本上,您需要为每个服务创建多个端点。
您通常会为每个主要实体(如 IInvoice、IPurchase、ISalesOrder)创建不同的服务。
另一种选择是将查询与命令分开。您可以为每个主要实体提供一个命令服务,并实施业务操作,只接受他们执行操作所需的数据(避免类似 CRUD 的操作);以及一个以客户端要求的格式返回数据的查询服务。这意味着命令部分使用了底层的领域模型/业务层;而查询服务直接对数据库进行操作(绕过业务,查询不需要)。这大大简化了您的查询并使其更加灵活(仅返回客户需要的内容)。
您似乎在 DataContract(s) 和 ServiceContract(s) 之间混用。您可以拥有一个 ServiceContract 和多个 DataContract,这将完全满足您的需求。
事实是,拆分 WCF 服务或任何服务是一种平衡行为。原则是您希望在仍然考虑性能的同时保持对复杂性的下行压力。
您创建的服务越多,您必须编写的配置就越多。此外,您将增加需要在客户端创建和维护的代理类的数量。
在一项服务上放置过多的 ServiceContract 会增加生成和使用代理所需的时间。但是,如果您最终只在合同上执行一两个操作,那么您将增加系统的复杂性而几乎没有什么收获。这不是一个科学的处方,但一个好的经验法则可以说是每个 ServiceContract 大约 10-20 个 OperationContracts。
类耦合当然是一个考虑因素,但你真的在处理单独的问题吗?这取决于你的系统做什么,但大多数系统只处理几个关注领域,所以拆分事情实际上可能不会减少类耦合。
另一件事要记住,这非常重要,那就是始终使您的方法尽可能通用。WCF 处理 DataContracts 是有原因的。DataContracts 意味着只要 DataContracts 是已知的,您就可以向服务器发送和从服务器发送任何对象。
因此,例如,您可能有 3 个 OperationContract:
[OperationContract]
Person GetPerson(string id);
[OperationContract]
Dog GetDog(string id);
[OperationContract]
Cat GetCat(string id);
但是,只要这些都是已知类型,您就可以将它们合并到一个操作中,例如:
[OperationContract]
IDatabaseRecord GetDatabaseRecord(string recordTypeName, string id);
归根结底,这是设计服务合同时要考虑的最重要的事情。如果您使用 DataContract 序列化之类的序列化方法,这适用于 REST。
最后,每隔几个月回顾一下您的 ServiceContracts 并删除客户未使用的操作。这又是一件大事!
您应该根据预期的负载、所需的可扩展性和未来的前景来做出决定。当您为客户编写“小型客户端服务器应用程序”时,它并没有明确说明手头开发的预期用途。大先生的回答也必须考虑。
欢迎您提出进一步的问题,并以具体数据或有关当前情况的细节为后盾。谢谢。