Domain driven design 领域驱动设计中的搜索上下文

Domain driven design 领域驱动设计中的搜索上下文,domain-driven-design,Domain Driven Design,如何按照域驱动的设计对类似堆栈溢出的网站的搜索上下文进行建模 假设在我的领域中,我有三种类型的实体,如问题、答案和问题标签。 我必须对搜索上下文进行建模,使其能够接受搜索字符串并返回匹配的问题、答案和标记 我想了解的是,在搜索上下文中,搜索将只是一个ddd服务,它将执行搜索,或者可能有一些实体、聚合等 可以假设匹配算法是简单的sql查询 在搜索范围内的上下文中,您可能不需要整个DDD战术模式阵列,不需要。除非您计划存储搜索首选项或创建搜索模板,也许-即使这样,它看起来也很粗糙 但是,设计搜索上下

如何按照域驱动的设计对类似堆栈溢出的网站的搜索上下文进行建模

假设在我的领域中,我有三种类型的实体,如问题、答案和问题标签。 我必须对搜索上下文进行建模,使其能够接受搜索字符串并返回匹配的问题、答案和标记

我想了解的是,在搜索上下文中,搜索将只是一个ddd服务,它将执行搜索,或者可能有一些实体、聚合等


可以假设匹配算法是简单的sql查询

在搜索范围内的上下文中,您可能不需要整个DDD战术模式阵列,不需要。除非您计划存储搜索首选项或创建搜索模板,也许-即使这样,它看起来也很粗糙


但是,设计搜索上下文如何索引或访问其他BC的数据可能是一个更复杂的问题。

您需要在查询服务中实现搜索

CQRS非常适合DDD,因为当您想要更新模型时,您会使用命令,例如在您的案例中:

AnswerToQuestionCommand、PostNewQuestionCommand等

这些命令被发送到应用程序服务,应用程序服务更新实体,实体又发送DomainEvent,该事件在Hexagon体系结构中被拦截并更新搜索索引。您可以在六边形体系结构中实现这一点:通过调用专用于维护同一事务中的索引的服务。例如,如果索引也被更新,则可以考虑持久化的实体。我会走这条路

下面是Spring@TransactionalEventListener的示例

@Component
public class ProjectRenamedListener {

    @Value("${spring.jpa.properties.hibernate.search.default.indexBase:target}")
    private String indexBase;

    private final Logger logger = LoggerFactory.getLogger(ProjectRenamedListener.class);

    @TransactionalEventListener
    public void projectRenamed(final ProjectRenamed event) {

        try (final StandardAnalyzer analyzer = new StandardAnalyzer();
             final Directory directory = NIOFSDirectory.open(Paths.get(indexBase, ProjectData.class.getName()));
             final IndexWriter writer = new IndexWriter(directory, new IndexWriterConfig(analyzer))){

            final Document document = new Document();
            document.add(new StringField("project_id", event.projectId().identity(), Field.Store.YES));
            document.add(new StringField("tenant_id", event.tenant().identity(), Field.Store.YES));
            document.add(new TextField("name", event.name(), Field.Store.YES));
            writer.updateDocument(new Term("project_id", event.projectId().identity()), document);
        } catch (IOException e) {
            logger.warn("Unable to update index for project name.", e.getMessage());
        }
    }
}
当您需要查询您的问题、答案等时,您需要通过查询服务。您可以请求索引,并加载要返回给客户机的实体

这样做的好处是,当您修改和查询它时,它不必是同一个对象。由于代码重复,开始时听起来有点奇怪,但从长远来看,它显然是一个优越的解决方案,因为在读/查询操作(经常发生并且应该很快)和写操作(不应该经常发生并且不必特别快)之间没有耦合

我建议使用Vaughn Vernon query services()的方法,我使用lucene作为查询服务,下面是一些代码:

@PreAuthorize("hasAuthority('Administrator')")
public Page<ProjectData> searchProjectsData(
        final String tenantId,
        final String queryText,
        final Pageable pageable) {

    if (!StringUtils.isEmpty(tenantId)) {
        return this.searchProjectsDataOfTenant(tenantId, queryText, pageable);
    }

    final StandardAnalyzer analyzer = new StandardAnalyzer();
    final QueryBuilder builder = new QueryBuilder(analyzer);
    final Query query = builder.createPhraseQuery("name", queryText);

    try {
        final IndexReader reader = DirectoryReader.openIfChanged(this.directoryReader);
        final IndexSearcher searcher = new IndexSearcher(reader);
        final TopDocs documents = searcher.search(query, pageable.getPageSize());
        return this.fetchProjectsData(searcher, documents, pageable);
    } catch (IOException e) {
        throw new RuntimeException(String.format("Unable to search project for query: %s", queryText), e);
    }
}
@预授权(“hasAuthority('Administrator'))
公共页面搜索项目数据(
最终字符串tenantId,
最后一个字符串queryText,
最终可分页(可分页){
如果(!StringUtils.isEmpty(tenantId)){
返回此.searchprojectsdataftenant(tenantId、queryText、pageable);
}
最终StandardAnalyzer=新的StandardAnalyzer();
最终QueryBuilder生成器=新的QueryBuilder(分析器);
最终查询Query=builder.createPhraseQuery(“名称”,queryText);
试一试{
final IndexReader reader=DirectoryReader.openIfChanged(this.DirectoryReader);
最终索引搜索器搜索器=新索引搜索器(读卡器);
最终TopDocs文档=searcher.search(查询,pageable.getPageSize());
返回此.fetchProjectsData(搜索器、文档、可分页);
}捕获(IOE异常){
抛出新的RuntimeException(String.format(“无法在项目中搜索查询:%s”,queryText),e);
}
}

我认为你的问题太宽泛了。你能说得更具体些吗?也许提供一些代码并问一个特定的问题。我不确定要提供什么细节。我的问题有点宽泛,我想了解搜索上下文中的顶级类。我对描述做了一点修改,可能会有帮助,或者请您建议我可以提供哪些详细信息。我想您要求的是stack overflow的知识产权。有很多关于DDD的好书。如果他们愿意,也许你可以买一个?我正在读沃恩·弗农(Vaughn Vernon)的《实现领域驱动的设计书》,书中没有澄清我的疑问,这就是为什么我在这里问这个问题。我只是想把搜索上下文的建模作为实现DDD的一个例子。我认为其他BC可以在可搜索实体发生更改时发出事件,搜索上下文可以订阅这些事件以保持自身的更新。假设我们没有存储任何首选项或模板。我认为可以有一个名为“searchRequest”的对象,它可以包含“搜索查询”、“页码”、“排序器”、“应用过滤器”等数据。使用这些数据,搜索存储库(但只有聚合应该有存储库)可以生成搜索结果。但这个“seachRequest”对象是什么,它不是实体(没有标识)。还有别的办法吗?活动部分或多或少是我们目前团队的工作。搜索服务监听这些事件,提取感兴趣的数据并更新其内部Lucene索引。正如我所说的,搜索服务不需要使用DDD模式,因此不需要存储库、实体等。因为它本质上是一个只读上下文,您可以从CQR中使用简单的读取模型所做的工作中获得灵感。编辑:在六边形体系结构中添加了搜索维护索引,带有触发索引更新的DomainEvent。就DDD而言要好得多。