ASP.NETMVC Web应用程序中的控制器应该调用存储库、服务,还是两者都调用?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (61)

我的ASP.NET MVC Web应用程序中的控制器开始变得有点臃肿的业务逻辑。Web上的示例都显示简单的控制器操作,只需将数据从存储库中提取出来并传递给视图即可。但是如果你还需要支持业务逻辑呢?

比如说,一个满足订单的行为也需要发送一封电子邮件。我是否将这一点放在控制器中,并将此逻辑复制/粘贴到任何其他同样符合命令的操作?我的第一个直觉就是创建一个像OrderFulfillerService这样的服务来处理所有这些逻辑,并让控制器动作调用它。但是,对于像从数据库中检索用户或订单列表这样的简单操作,我想直接与存储库交互,而不是将该调用包含在服务中。

这是一个可接受的设计模式?控制器操作在需要数据访问时需要业务逻辑和存储库时调用服务?

提问于
用户回答回答于

我不确定为此使用服务。

据我了解,DDD的原理之一(我现在正在读)是域对象被组织成聚合,并且当你创建聚合根的实例时,它只能直接处理Aggregate内的对象(以帮助保持清晰的责任感)。

创建聚合应执行任何不变量等。

以Customer类的示例为例,Customer可能是Aggregate根,而Aggregate中的另一个类可能是Address。

现在,如果您想创建一个新的客户,您应该可以使用Customer构造函数或工厂来完成此操作。这样做应该返回在Aggregate边界内完全有效的对象(所以它不能处理产品,因为它们不是Aggregate的一部分,但它可以处理地址)。

数据库是一个次要问题,仅用于将聚合持久化到数据库或从数据库中检索数据库。

为了避免直接与数据库连接,您可以创建一个Repository接口(如所讨论的),给定一个Customer实例(其中包含对Address的引用)应该能够将Aggregate持久化到数据库。

重点在于Repository接口是您的领域模型/层的一部分(不是存储库的实现)。另一个因素是版本库可能最终应该调用与创建新对象相同的“创建”方法(以维护不变量等)。如果你使用的是构造函数,这很简单,因为当数据库从数据库“创建”对象时,最终你会调用构造函数。

应用程序层可以直接与域(包括存储库接口)通信。

所以如果你想创建一个对象的新实例,你可以

Customer customer = new Customer();

如果应用程序需要从存储库中检索一个客户实例,我没有什么特别的理由让它不调用...

Customer customer = _custRepository.GetById(1)

或者

Customer customer = _custRepository.GetByKey("AlanSmith1")

最终,它将最终得到一个Customer对象的实例,该实例在其自身的限制和规则内运行,就像直接创建新的Customer对象一样。

我认为服务应该保留,因为当你试图使用的“事物”不是一个对象时。大多数规则(约束等)可以写成域对象本身的一部分。

我正在阅读的DDD Quickly pdf中有一个很好的例子。在那里,他们对书架对象有一个限制,因此只能添加与书架可以包含的书一样多的书。

在将Book添加到BookShelf的Book对象集合之前,调用BookShelf对象上的AddBook方法检查该空间是否可用。一个简单的例子,但业务规则是由域对象本身强制执行的。

用户回答回答于

控制器(在MVC项目中)应该在服务项目中调用对象。服务项目是处理所有业务逻辑的地方。

这是一个很好的例子:

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();
}

现在..什么是服务项目(或什么是产品服务)?这是一个带有业务逻辑的类库。例如。

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();
        }
    }
}

扫码关注云+社区

领取腾讯云代金券