Aar*_*ins 2 c# wcf domain-driven-design dependency-injection ninject
我正在尝试实现一个概念验证,我在其中编写了一个模块(让我们说这个讨论的目的论坛)遵循领域驱动设计指南,并且将有一个可插拔的存储库,整个模块可以在Web服务器本地插入(本地托管的dll)或通过WCF服务.
遵循域驱动设计指南,我会有一组业务对象,可以像这样编写:
public class Forum
{
readonly IRepository _forumRepository;
public Forum(IRepository forumRepository)
{
_forumRepository = forumRepository;
}
public string Name { get; set; }
public void Save()
{
_forumRepository.SaveForum(this);
}
}
Run Code Online (Sandbox Code Playgroud)
假设Web应用程序需要创建一个新论坛.
如果模块通过dll文件本地托管,这一切都很好.Web应用程序代码将简单地使用Ninject(或任何DI)来实例化业务对象并调用Save方法.
但是,如果实施者想在两者之间引入服务层呢?假设他们想在应用程序和数据库层之间引入WCF服务层,因为他们希望物理架构是Web Server - > App Server - > DB Server.显然,论坛模块dll将由Web服务器和应用服务器托管.但是,该模块不再可用.
如果Web服务器使用存储库注入实例化对象并将其传递到WCF服务层,则即使我允许将业务对象序列化,_forumRepository字段也会丢失.
实现者应该能够让应用服务器选择存储库.那么,有了这个要求,在从Web服务器接收到已经实例化的对象之后,实现者如何在应用服务器端注入存储库?有没有办法告诉WCF服务在反序列化过程中实例化对象时注入存储库?
我阅读了文章,如何在WCF服务中使用依赖注入(Ninject),并查看了示例解决方案,但这仅演示了如何将存储库直接注入服务.它似乎没有解决我在这里讨论的问题.
以下是我如何正确编写代码的例子.正如我之前所说,这里的主要问题是我不知道如何将存储库注入服务端的域对象.
应用程序代码(Web App或Windows App):
Forum newForum = new Forum();
//Set some properties on newForum.
IKernel kernel = new StandardKernel(); //Instantiate ninject kernel.
//Yes, I know using the using statement means that IService needs to derive from IDisposable. A local service would just have an empty implementation for IDisposable since there would be nothing to clean up and this would allow different service architectures to be plugged into the application.
using (IForumService forumService = kernel.Get<IForumService>()) //Get concrete implementation bound to IForumService via ninject.
{
//Send forum to service layer for local OR WCF processing (on an app server)
forumService.SaveForum(newForum);
}
Run Code Online (Sandbox Code Playgroud)
服务代码(这是问题):
public class WCFForumService : IForumService
{
public void SaveForum(Forum forum)
{
//PROBLEM SCENARIO: How do I now inject the repository I want to use since I already have an instance of forum?
//Can I make WCF inject the repository when it is deserializing the forum object before passing it into this method?
forum.Save();
}
}
Run Code Online (Sandbox Code Playgroud)
如果可能的话,我宁愿不强迫我的论坛模块的实施者为每个域对象创建DTO.这违反了DRY原则.如果我可以创建我的域对象,那将是非常好的,因此它们是通用的和/或可扩展的,足以用作DTO本身,只需很少的努力.这样,我可以使用数据注释对域对象进行业务规则验证,并且实现者可以在Web窗体,MVC3或Silverlight项目中使用这些对象,而无需手动重新执行所有验证.
编辑:以上是完全错误的方法.编辑以演示更有效的方法.
public class Forum
{
public Forum(string name)
{
Name = name;
}
public string Name { get; set; }
}
public interface IForumRepository : IRepository<Forum>
{
void Add(Forum forum);
Forum this[object id] { get; set; }
}
//Client Code (Called from the WCF service hosting the domain)
public class WCFAppService
{
public void SaveForum(ForumDTO forumInfo)
{
IKernel kernel = StandardKernel();
IForumRepository repository = kernel.Get<IForumRepository>();
Forum forum = repository[forumInfo.ID];
if (forum != null)
{
repository[forumInfo.ID] = forumInfo.CopyTo(forum); //Save the Forum to the db.
}
else
{
repository.Add(ForumFactory.CreateFrom(forumInfo)); //Insert the Forum into the db.
}
}
}
Run Code Online (Sandbox Code Playgroud)
我尝试这个原型时遇到的大部分问题是我在尝试学习DDD的过程中过多地关注基础设施问题(即DI,服务器架构等).我对DDD的了解使我得出的结论是,在试图弄清楚如何构建DDD解决方案时,忘记架构和技术,直到掌握DDD将为您做什么为止.我做这个笔记是因为在我目前正在研究的实际DDD项目中,DI似乎是一种不必要的复杂功能.这就是DDD如何使您的代码变得简单.
通过将存储库传递到Web服务器,您将跳过图层.没有什么可以阻止您调用保存或以业务规则不允许的方式操作数据.此外,您不想关心Web服务器上的数据库详细信息.这是应用服务器的责任.
在应用程序和Web服务器之间应用程序和数据库服务器之间应该有完全不同的界面.应用服务器的界面应包含您要在Web服务器上执行的操作.
假设你有一个银行帐户系统.并希望在两个帐户之间执行交易.在这种情况下,您的解决方案,你会派两个所涉及的accouts到Web服务器,并降低由转移量的第一个帐户的余额,并增加相同的量,第二个.然后它将帐户发送回应用程序服务器以进行保存.
但这是错误的.什么是正确的是提供提供了一个交易的方法,该方法所涉及的账户号码和要传输的量和完整的操作应用服务器上做应用服务器上的服务.
在您的示例中,您应该提供一种方法ChangeForumName(int forumId, string newName).现在,您可以确保名称必须满足的规则.不是空的.