DDD가없고 또는없는 ESQ없는 CQRS-쓰기 모델이란 무엇이며 읽기 모델은 무엇입니까?


11

내가 이해하는 한, CQRS의 기본 아이디어는 명령과 쿼리를 처리하기위한 두 가지 데이터 모델이 있다는 것입니다. 이를 "모델 쓰기"및 "모델 읽기"라고합니다.

Twitter 응용 프로그램 복제의 예를 생각해 봅시다. 명령은 다음과 같습니다.

  • 사용자는 스스로 등록 할 수 있습니다. CreateUserCommand(string username)방출UserCreatedEvent
  • 사용자는 다른 사용자를 팔로우 할 수 있습니다. FollowUserCommand(int userAId, int userBId)방출UserFollowedEvent
  • 사용자는 게시물을 만들 수 있습니다. CreatePostCommand(int userId, string text)방출PostCreatedEvent

위에서 "이벤트"라는 용어를 사용하지만 '이벤트 소싱'이벤트를 의미하지는 않습니다. 읽기 모델 업데이트를 트리거하는 신호를 의미합니다. 이벤트 상점이 없으며 지금까지 CQRS 자체에 집중하고 싶습니다.

그리고 여기 쿼리가 있습니다 :

  • 사용자는 게시물 목록을 확인해야합니다. GetPostsQuery(int userId)
  • 사용자는 팔로어 목록을 확인해야합니다. GetFollowersQuery(int userId)
  • 사용자는 자신이 따르는 사용자 목록을 확인해야합니다. GetFollowedUsersQuery(int userId)
  • 사용자는 모든 친구 활동의 로그인 "친구 피드"를 볼 필요가 있습니다 ( "친구 John이 방금 새 게시물을 만들었습니다"). GetFriedFeedRecordsQuery(int userId)

처리하려면 CreateUserCommand해당 사용자가 이미 존재하는지 알아야합니다. 따라서이 시점에서 필자의 쓰기 모델에는 모든 사용자 목록이 있어야합니다.

처리하려면 FollowUserCommanduserA가 이미 userB를 따르는 지 여부를 알아야합니다. 이 시점에서 필자의 쓰기 모델에 모든 사용자-팔로우-사용자 연결 목록이 필요합니다.

그리고 마지막으로, 처리하기 위해 CreatePostCommand와 같은 명령이 없기 때문에 다른 것이 필요하다고 생각하지 않습니다 UpdatePostCommand. 내가 가지고 있다면 게시물이 있는지 확인해야하므로 모든 게시물 목록이 필요합니다. 그러나이 요구 사항이 없기 때문에 모든 게시물을 추적 할 필요는 없습니다.

질문 # 1 : "모델 작성"이라는 용어를 사용하는 것이 실제로 올바른가요? 또는 ES의 경우 "모델 쓰기"가 항상 "이벤트 저장소"를 의미합니까? 그렇다면 명령을 처리해야하는 데이터와 쿼리를 처리해야하는 데이터 사이에 어떤 종류의 분리가 있습니까?

를 처리하려면 GetPostsQuery모든 게시물 목록이 필요합니다. 이것은 내 읽기 모델에 모든 게시물 목록이 있어야 함을 의미합니다. 을 듣고이 모델을 유지하겠습니다 PostCreatedEvent.

모두를 처리하기 위해 GetFollowersQuery그리고 GetFollowedUsersQuery, 나는 사용자 사이의 모든 연결 목록을해야합니다. 이 모델을 유지하기 위해을 들어 보겠습니다 UserFollowedEvent. 여기입니다 질문 # 2 : 여기 연결의 쓰기 모델의 목록을 사용하는 경우는 실질적으로 OK인가? 아니면 나중에 별도의 읽기 모델을 만들어야하는데, 나중에 쓰기 모델보다 자세한 내용이 필요할 수 있기 때문입니다.

마지막으로, 처리 GetFriendFeedRecordsQuery하려면 다음이 필요합니다.

  • 들어 봐 UserFollowedEvent
  • 들어 봐 PostCreatedEvent
  • 어떤 사용자가 다른 사용자를 팔로우하는지 파악

사용자 A가 사용자 B를 따르고 사용자 B가 사용자 C를 따르기 시작하면 다음 레코드가 나타납니다.

  • 사용자 A의 경우 : "친구 B 사용자가 사용자 C를 팔로우하기 시작했습니다."
  • 사용자 B의 경우 : "방금 사용자 C를 시작했습니다."
  • 사용자 C의 경우 : "사용자 B가 이제 당신을 따르고 있습니다"

여기의 질문 # 3 : 나는 연결 목록을 얻기 위해 무엇을 사용해야 모델? 쓰기 모델을 사용해야합니까? 나는 모델을 읽을 사용해야합니다 - GetFollowersQuery/를 GetFollowedUsersQuery? 아니면 GetFriendFeedRecordsQuery모델 자체를 처리하고 UserFollowedEvent모든 연결의 자체 목록을 유지해야합니까?


CQRS 시스템에서 쿼리 데이터 모델 및 명령 데이터 모델은 다른 데이터베이스의 데이터를 소비 할 수 있습니다. 그리고 두 모델 모두 서로 독립적으로 살 수 있습니다 (다른 앱). 즉, 대답은 "평소와 같이"의존합니다. 그것은 수도 관심
LAIV

답변:


7

그렉 영 (2010)

CQRS는 이전에 하나만 있던 두 개의 개체를 만드는 것입니다.

Bertrand Meyer의 Command Query Separation으로 생각하면 모델을 명령을 지원하는 인터페이스와 쿼리를 지원하는 두 가지 인터페이스가 있다고 생각할 수 있습니다.

interface IChangeTheModel {
    void createUser(string username)
    void followUser(int userAId, int userBId)
    void createPost(int userId, string text)
}

interface IDontChangeTheModel {
    Iterable<Post> getPosts(int userId)
    Iterable<Follower> getFollowers(int userId)
    Iterable<FollowedUser> getFollowedUsers(int userId)
}

class TheModel implements IChangeTheModel, IDontChangeTheModel {
    // ...
}

Greg Young의 통찰은 이것을 두 개의 개별 객체로 분리 할 수 ​​있다는 것입니다

class WriteModel implements IChangeTheModel { ... }
class ReadModel  implements IDontChangeTheModel {...}

객체를 분리하면 이제 객체 상태를 유지하는 데이터 구조를 메모리에 분리하여 각 경우에 맞게 최적화 할 수 있습니다. 또는 쓰기 상태와 별도로 읽기 상태를 저장 / 지속합니다.

질문 # 1 : "모델 쓰기"라는 용어를 사용하는 것이 실제로 올바른가요? 또는 ES의 경우 "모델 쓰기"가 항상 "이벤트 저장소"를 의미합니까? 그렇다면 명령을 처리해야하는 데이터와 쿼리를 처리해야하는 데이터 사이에 어떤 종류의 분리가 있습니까?

WriteModel이라는 용어는 일반적으로 모델의 변경 가능한 표현 (즉, 지속성 저장소가 아닌 객체)으로 이해됩니다.

TL; DR : 귀하의 사용이 정상이라고 생각합니다.

여기에 질문 # 2가 있습니다 : 쓰기 모델의 연결 목록을 여기에서 사용하면 실제로 괜찮습니까?

그건 "좋아". 개념적으로 동일한 모델을 공유하는 읽기 모델과 쓰기 모델에는 아무런 문제가 없습니다.

실제로 모델에 대한 쓰기는 일반적으로 원자 적이 지 않기 때문에 한 스레드가 모델의 상태를 수정하려고 시도하는 동안 두 번째 스레드가 모델을 읽으려고 할 때 문제가 발생할 수 있습니다.

질문 # 3 : 연결 목록을 얻기 위해 어떤 모델을 사용해야합니까? 쓰기 모델을 사용해야합니까? 읽기 모델을 사용해야합니까-buyerQueryQuery / GetFollowedUsersQuery를 사용해야합니까? 아니면 GetFriendFeedRecordsQuery 모델 자체가 UserFollowedEvent를 처리하고 모든 연결의 자체 목록을 유지하도록해야합니까?

쓰기 모델을 사용하는 것이 정답입니다.

각각 특정 사용 사례에 맞게 조정 된 다중 읽기 모델 작성은 전적으로 합리적입니다. "읽기 모델"이라고 말하지만 각각 특정 사용 사례에 최적화 된 읽기 모델이 많을 수 있으며 이해가되지 않는 경우는 구현하지 않습니다.

예를 들어, 키-값 저장소를 사용하여 일부 쿼리와 다른 쿼리에 대한 그래프 데이터베이스 또는 해당 쿼리 모델이 적합한 관계형 데이터베이스를 지원하기로 결정할 수 있습니다. 코스 말.

패턴을 배우는 특정 상황에서는 디자인을 "단순하게"유지하는 것이 좋습니다. 쓰기 모델의 데이터 구조를 공유하지 않는 하나의 읽기 모델이 있습니다.


1

하나의 개념적 데이터 모델이 있다고 생각하는 것이 좋습니다.

그런 다음 쓰기 모델은 트랜잭션 업데이트에 최적화 된 해당 데이터 모델의 구체화입니다. 때때로 이것은 정규화 된 관계형 데이터베이스를 의미합니다.

읽기 모델은 응용 프로그램에 필요한 쿼리를 수행하기 위해 최적화 된 동일한 데이터 모델을 구체화 한 것입니다. 조인 수가 적은 쿼리를 처리하기 위해 의도적으로 비정규 화되었지만 관계형 데이터베이스 일 수 있습니다.


(# 1) CQRS의 경우 쓰기 모델이 이벤트 저장소 일 필요는 없습니다.

(# 2) 읽기 모델이 쓰기 모델에없는 것을 저장하지는 않을 것입니다. CQRS에서는 읽기 모델을 모델을 작성하십시오.

(# 3) CQRS에서 쿼리는 읽기 모델을 기준으로해야합니다. CQRS를 따르지 않고 괜찮습니다.


요약하면 하나의 개념적 데이터 모델 만 있습니다. CQRS는 명령 네트워크 및 기능을 쿼리 네트워크 및 기능과 분리합니다. 분리 된 경우 쓰기 모델과 읽기 모델은 성능 최적화로 매우 다양한 기술을 사용하여 호스팅 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.