我的ASP.NET MVC应用中的控制器开始因为业务逻辑而变得有点臃肿。web上的示例都显示了简单的控制器操作,这些操作只是将数据从存储库中拉出并将其传递给视图。但是,如果您还需要在此基础上支持业务逻辑呢?
例如,执行订单的操作还需要发送一封电子邮件。我是否将其粘贴到控制器中,并将此逻辑复制/粘贴到也满足订单的任何其他操作?我的第一个直觉是创建一个像OrderFulfillerService这样的服务,它负责处理所有这些逻辑,并让控制器操作调用这些逻辑。但是,对于简单的操作,如从数据库中检索用户或订单列表,我希望直接与存储库交互,而不是将调用包装在服务中。
这是一个可接受的设计模式吗?控制器操作在需要业务逻辑和存储库时调用服务,而只需要数据访问?
发布于 2008-12-04 01:56:55
您的控制器(在MVC项目中)应该调用Service项目中的对象。服务项目是处理所有业务逻辑的地方。
一个很好的例子是:
public ActionResult Index()
{
ProductServices productServices = new ProductServices();
// top 10 products, for example.
IList<Product> productList = productServices.GetProducts(10);
// Set this data into the custom viewdata.
ViewData.Model = new ProductViewData
{
ProductList = productList;
};
return View();
}
或者使用依赖注入(我最喜欢的)
// Field with the reference to all product services (aka. business logic)
private readonly ProductServices _productServices;
// 'Greedy' constructor, which Dependency Injection auto finds and therefore
// will use.
public ProductController(ProductServices productServices)
{
_productServices = productServices;
}
public ActionResult Index()
{
// top 10 products, for example.
// NOTE: The services instance was automagically created by the DI
// so i din't have to worry about it NOT being instansiated.
IList<Product> productList = _productServices.GetProducts(10);
// Set this data into the custom viewdata.
ViewData.Model = new ProductViewData
{
ProductList = productList;
};
return View();
}
现在..。什么是服务项目(或者什么是ProductServices)?这是一个包含业务逻辑的类库。例如。
public class ProductServices : IProductServices
{
private readonly ProductRepository _productRepository;
public ProductServices(ProductRepository productRepository)
{
_productRepository = productRepository;
}
public IList<Product> GetProducts(int numberOfProducts)
{
// GetProducts() and OrderByMostRecent() are custom linq helpers...
return _productRepository.GetProducts()
.OrderByMostRecent()
.Take(numberOfProducts)
.ToList();
}
}
但这一切可能都是如此的核心和困惑...因此,ServiceProduct类的一个简单版本可以是(但我并不真正推荐)……
public class ProductServices
{
public IList<Product> GetProducts(int numberOfProducts)
{
using (DB db = new Linq2SqlDb() )
{
return (from p in db.Products
orderby p.DateCreated ascending
select p).Take(10).ToList();
}
}
}
这就对了。您可以看到,所有逻辑都在Service项目中,这意味着您可以在其他地方重用该代码。
我从哪里学到这个的?
来自Rob Conery的MVC StoreFront媒体和tutorials。自从切片面包以来最好的东西。他的教程用完整的解决方案代码示例很好地解释了(我做了什么)。他使用了依赖注入,现在我已经看到了他在MVC中是如何使用它的。
HTH。
发布于 2009-01-23 10:38:32
我不确定是否要使用服务来实现这一点。
据我所知,DDD的原则之一(我现在正在阅读)是将域对象组织到Aggregate中,并且当您创建Aggregate根的实例时,它只能直接处理Aggregate中的对象(以帮助维护清晰的责任感)。
创建聚合应该强制执行任何不变式等。
以Customer类为例,Customer可能是聚合根,聚合中的另一个类可能是Address。
现在,如果您想创建一个新客户,您应该能够使用Customer构造函数或工厂来完成此操作。这样做应该返回一个在聚合边界内具有完整功能的对象(因此它不能处理产品,因为这些产品不是聚合的一部分,但它可以处理地址)。
数据库是次要关注点,仅在将聚合持久存储到数据库或从数据库中检索它时才起作用。
为了避免直接与数据库交互,您可以创建一个Repository接口(如上所述),在给定Customer实例(其中包括对Address的引用)的情况下,该接口应该能够将聚合持久存储到数据库。
重点是Repository接口是域模型/层的一部分( Repository的实现不是)。另一个因素是,存储库可能最终应该调用相同的"create“方法,就像您正在创建一个新对象一样(以维护不变量等)。如果你使用的是一个构造函数,这就足够简单了,因为当存储库从数据“创建”对象时,你最终会调用构造函数。
应用层可以直接与域通信(包括存储库接口)。
因此,如果你想创建一个对象的新实例,你可以例如
Customer customer = new Customer();
如果应用程序需要从存储库中检索customer的实例,我想不出什么特别的理由让它不调用...
Customer customer = _custRepository.GetById(1)
或者..。
Customer customer = _custRepository.GetByKey("AlanSmith1")
最终,它将以Customer对象的一个实例结束,该实例在它自己的限制和规则内运行,就像它直接创建新的Customer对象一样。
我认为当你试图使用的“东西”不是一个对象时,应该保留服务。大多数规则(约束等)都可以作为域对象本身的一部分来编写。
一个很好的例子就是我目前正在阅读的DDD快速pdf。在其中,它们对书架对象有一个约束,借此,您只能添加书架所能容纳的书。
在BookShelf对象上调用AddBook方法可以在将图书添加到书架的图书对象集合之前检查是否有可用的空间。这是一个简单的示例,但是业务规则是由域对象本身强制执行的。
顺便说一句,我并不是说上面的任何一个都是正确的!我现在正试着把这一切都弄清楚!
发布于 2008-12-04 00:24:01
在业务服务中看到很多这样的东西可能看起来很烦人:
public Customer GetCustomer(int id)
{
return customerRepository.Get(id);
}
有一种强烈的冲动想要绕过这项服务,这是很自然的。但从长远来看,允许您的业务服务介于控制器和存储库之间是更好的选择。
现在,对于一个非常简单的CRUD类型的应用程序,您可以让您的控制器直接使用存储库,而不是通过业务服务。您仍然可以拥有类似于EmailerService的东西,但当涉及到获取和处理实体时,最好不要在控制器中混合和匹配业务服务和存储库调用。
至于让实体(业务对象)调用服务或任何基础设施组件,我不会这么做。我更喜欢让实体保持POCO和无依赖关系。
https://stackoverflow.com/questions/338816
复制相似问题