2017-11-05 1 views
0

스택 오버플로와 같은 웹 사이트의 검색 컨텍스트를 도메인 기반 디자인에 따라 어떻게 모델링 할 수 있습니까?도메인 기반 디자인의 검색 컨텍스트

내 도메인에서 질문, 답변, 질문 모음과 같은 세 가지 유형의 항목이 있다고 가정 해 보겠습니다. 검색 문자열을 받아들이고 일치하는 질문, 답변 및 태그를 반환하는 방식으로 검색 컨텍스트를 모델링해야합니다.

는 등 집계

일치하는 알고리즘은 가정 할 수있다

내가 검색 맥락에서, 검색은 검색을 수행하거나 일부 개체가있을 수 있습니다 단지 주차 대행 서비스가 될 것입니다, 이해 할

입니다 것을 간단한 SQL 같은 쿼리.

+0

내가 당신의 질문이 너무 광범위하다고 판단 :

내가 쿼리 서비스에 본이 버논의 접근 방식 (https://github.com/VaughnVernon/IDDD_Samples)를 제안, 나는 쿼리 서비스에 대한 나의 부분에 루씬을 사용, 여기에 몇 가지 코드입니다. 더 자세하게 얘기해 주 시겠어요? 아마도 몇 가지 코드를 제공하고 특정 질문을 던지십시오. – w0051977

+0

제공 할 세부 정보가 확실하지 않습니다. 제 질문은 좀 더 광범위합니다. 검색 컨텍스트에서 최상위 클래스를 이해하고 싶습니다. 설명을 약간 변경했거나, 내가 제공 할 수있는 세부 정보를 도울 수 있거나 친절하게 제안 할 수 있습니다. –

+0

스택 오버 플로우의 지적 재산이라고 생각합니다. DDD에 관한 많은 훌륭한 책들이 있습니다. 만약 당신이 그들을 살 수 있다면? 학습 한 개념을 적용 할 수 있습니다. – w0051977

답변

0

아마도 검색 경계 된 문맥에 대해 DDD 전술 패턴의 전체 배열을 필요로하지 않을 것입니다. 검색 기본 설정을 저장하거나 검색 템플리트를 작성하려고 계획하는 경우를 제외하고 아마도 심지어 매우 혼란 스러울 것입니다.

검색 컨텍스트가 다른 BC의 데이터를 인덱싱하거나 액세스하는 방법을 디자인하는 것은 더 복잡한 문제 일 수 있습니다.

+0

검색 가능한 엔티티가 변경되고 검색 컨텍스트가 업데이트 될 때마다 다른 BC가 이벤트를 방출 할 수 있다고 생각했습니다. 환경 설정이나 템플릿을 저장하지 않는다고 가정 해 봅시다. "검색 쿼리", "페이지 번호", "분류기", "적용된 필터"등의 데이터를 포함 할 수있는 "searchRequest"라는 개체가있을 수 있다고 생각합니다. 검색 저장소를 사용하지만 집계에만 저장소가 있어야합니다.) 검색 결과를 생성 할 수 있습니다. 하지만이 "seachRequest"객체는 무엇이며 엔티티가 아닙니다 (신원이 없음). 다른 방법이 있습니까 ?? –

+0

이벤트 부분은 현재 팀에서하는 일입니다. 검색 서비스는 해당 이벤트를 수신하고 관심있는 데이터를 추출하며 내부 Lucene 인덱스를 업데이트합니다. 내가 말했던 것처럼 검색 서비스에 DDD 패턴을 사용할 필요가 없으므로 저장소, 엔티티 등을 사용할 필요가 없습니다. 본질적으로 읽기 전용 컨텍스트이므로 CQRS에서 수행 한 작업에서 간단한 읽기와 함께 영감을 얻을 수 있습니다 모델. – guillaume31

1

검색어 서비스에서 검색을 구현해야합니다. 당신은 귀하의 경우와 같은 명령을 사용하여 모델을 업데이트 할 때 때문에

CQRS은 DDD와 정말 잘 맞는 :

AnswerToQuestionCommand, PostNewQuestionCommand 등

이러한 명령은 응용 프로그램 서비스로 전송된다

이는 엔티티를 업데이트하고, 엔티티는 6 각형 아키텍처에서 인터셉트되어 검색 인덱스를 업데이트하는 DomainEvent를 보냅니다. 6 각형 아키텍처에서이를 수행 할 수 있습니다. 동일한 트랜잭션에서 인덱스를 유지 관리하는 전용 서비스를 호출합니다. 인덱스가 예를 들어 업데이트 된 경우 엔티티가 지속되는 것으로 간주합니다. 나는이 길로 갈 것이다. 당신은 당신이 쿼리 서비스를 통해 이동 등 질문, 답변, 쿼리해야하는 경우 여기

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

와 샘플입니다. 색인을 요청하고 클라이언트로 리턴 할 엔티티를로드하십시오.

이 기능의 장점은 수정하고 쿼리 할 때 동일한 개체 일 필요가 없다는 것입니다. 코드 복제로 인해 시작할 때 약간 이상하게 들리지만 장기간에 걸쳐 발생하는 읽기/질의 작업 사이의 결합이 빠르며 빠르지 만, 많이 발생해서는 안되는 쓰기 작업 그리고 특별히 ont는 매우 빠르다.

@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); 
    } 
} 
+0

EDIT : 인덱스 업데이트를 트리거하는 DomainEvent로 육각형 아키텍처의 검색 유지 관리 인덱스를 추가했습니다. DDD 기간면에서 훨씬 좋습니다. –

관련 문제