在域驱动设计中搜索上下文

时间:2017-11-05 15:37:48

标签: domain-driven-design

如何在域驱动设计之后对堆栈溢出等网站的搜索上下文进行建模?

让我们说在我的域名中,我有三种类型的实体,如问题,答案,问题标签。 我必须以接受搜索字符串并返回匹配的问题,答案和标签的方式对搜索上下文进行建模。

我想明白,在搜索环境中,搜索只是一个执行搜索的ddd服务,或者可能存在一些实体,聚合等。

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

2 个答案:

答案 0 :(得分:1)

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

CQRS非常适合DDD,因为当您想要更新模型时,您可以使用命令,例如在您的情况下:

AnswerToQuestionCommand,PostNewQuestionCommand等

这些命令被发送到您的应用程序服务,该服务更新实体,实体又发送一个DomainEvent,它在六边形体系结构中被拦截并更新搜索索引。您可以在六边形体系结构中执行此操作:通过调用专用于在同一事务中维护索引的服务。如果索引也已更新,您认为实体是持久的。我会这样走。

以下是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在查询服务上的方法(https://github.com/VaughnVernon/IDDD_Samples),我使用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);
    }
}

答案 1 :(得分:0)

对于搜索有界的上下文,你可能不需要整套DDD战术模式,没有。除非您计划存储搜索偏好或创建搜索模板,否则 - 即使这样,它似乎非常CRUD。

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