1

我用我的asp.net核心web api使用弹性搜索。当涉及到仓库责任时,我不太清楚界线在哪里。存储库模式是如何真正完成的?

下面是我定义我的实现:

public SearchRespository: ISearchRespository<Product> 
{ 
    private ElasticClient _client 

    public async Task<ISearchResponse<Product>> SearchAsync(ISearchRequest request) 
    { 
     var response = _client.SearchAsync<Product>(request); 
     return await products; 
    }   

    . . . // others 
}   

在我的控制器:

public SearchController : Controller 
{ 
    private ISearchRespository _repo; 

    public SearchController(ISearchRespository repo) 
    { 
     _repo = repo; 
    } 

    public async Task<IActionResult> Search() 
    { 
     // build my search request from Request.Query 

     var response = await _client.SearchAsync(request); 
     var model = new SearchModel 
     { 
      Products = response.Documents; 
      Aggregations = response.Aggregations; 
     } 

     return Ok(model) 
} 

因为它代表的回购协议通过弹性响应为是。我的问题是我画了我的线吗?如果我只是将_client移动到我的控制器或移动建筑物请求并将model设置为_repo?你们如何让你的资料库正确?

+0

我建议你在codereview.stackexchange.com中提出这个问题 –

+0

那么,存储库的目的是抽象出持久性。我假设'ISearchResponse'和'ISearchRequest'是弹性搜索接口。不应该在'ISearchRespository'上定义它们,因为那么你的持久性会泄漏到你的业​​务/域层。在这里你应该使用自己的类,或者如果你的搜索请求足够简单,使用它的方法参数。当你的持久化类型泄漏到你的域中时,除了DRY – Tseng

+0

我的搜索请求涉及很多外,该存储库几乎没有用处,我已经定义了帮助类来构建它们。所以你建议我让'repo'返回我的'SearcModel'并让这些帮助者帮助我的'repo'? –

回答

4

你使用弹性搜索的事实应该是一个实现细节,尤其是控制器不应该知道的,所以你完全正确地将它从控制器中抽象出来。我经常看SOLID原则来了解我是否在正确的轨道上。如果我们看一下Dependency Inversion Principle,你会发现它引导我们走向一种也被称为Ports and Adapters的风格,这基本上意味着将外部工具的使用抽象出来(端口),并在应用程序的边界上您实施连接到该第三方的Adapter

因此,从依存倒置原则的意义上说,你走在正确的轨道上。

然而,有很多Martin Fowler's Repository Pattern试图解决的误解。的定义如下:使用用于访问域对象的集合状界面和数据映射层之间

介导。

这里需要说明的是,存储库旨在被领域层使用。

然而,存储库模式存在很多误用,因为许多开发人员开始将它用作查询的分组结构。存储库正如我所见 - 并非针对系统中的所有查询;但仅适用于需要的查询。这些查询支持域为系统上的突变做出决定。

然而,您的系统需要的大多数查询都不是这种查询。你的代码就是一个很好的例子,因为在这种情况下你可以完全跳过这个域,只做一个读操作。

这是不适合存储库的东西。我们可以通过再次将它与SOLID原则进行比较来验证。

比方说,我们有以下库接口:

public interface IUserRepository 
{ 
    User[] FindUsersBySearchText(string searchText, bool includeInactiveUsers); 
    User[] GetUsersByRoles(string[] roles); 
    UserInfo[] GetHighUsageUsers(int reqsPerDayThreshold); 
    // More methods here 
} 

这是一个典型的资源库抽象,你会看到开发人员编写。这种抽象是从SOLID原则角度看问题,这是因为:

  • 接口隔离原则被违反,因为接口是宽(有许多方法)和这些接口的消费者不得不依赖于法说他们不没用。
  • 违反单一责任原则,因为存储库实施中的方法不具有高度一致性。与这些方法相关的唯一事实是它们属于同一概念或实体。
  • 该设计违反开放/封闭原则,因为几乎每次向系统添加查询时,都需要更改现有接口及其实现。每个接口至少有两个实现:一个真正的实现和一个测试实现。

像这样的设计也会导致很多痛苦,因为很难将横切关注点(比如安全性,审计,日志记录,缓存等)应用到线下。

所以这不是仓库模式打算解决的问题;这样的设计只是一个大的SOLID违规。

此处的解决方案是在系统中单独建模查询,并且不使用存储库,全部为。有很多关于这方面的文章,你可以阅读我的这篇文章here

如果我看看你的设计,它实际上与我在这里推广的设计有一些相似之处,因为你似乎有一个通用的查询方法,可以处理多种类型的查询。查询消息(您的ISearchRequest)似乎是Elastic Search特有的。这是你应该努力防止的事情,就像依赖倒置原则所说的那样。

+0

我仍然在寻找你的博客文章。非常有洞察力的阅读。快速的问题,在我的情况下,我正在构建弹性的“搜索请求”,而不是传递'searchText',所以在你的情况下,你会放置这样的逻辑? 'IQuery '或'IQueryHandler '? –

+0

@ no0b:我不确定我了解你的问题? TQuery是可能要求的定义(包含搜索标准的消息)。使用TQuery,IQueryHandler将执行搜索并构建TResult。 – Steven

+0

我还处于'更好设计'的开始部分:)。但我的意思是'FindUsersBySearchTextQuery.SearchText'是传递给'controller'并用于搜索包含它的用户名的搜索文本。有弹性,你应该从'SearchTest'建立弹性'SearchRequest'模型,以便与'_client.SearchAsync(searchRequest)'一起使用,所以我的问题是你会在'FindUsersBySearchTextQuery'或'FindUsersBySearchTextQueryHandler'或其他地方做到这一点。 –