이벤트 소싱 및 REST


17

Event Sourcing 디자인을 접하고 REST 클라이언트가 필요한 응용 프로그램에서 사용하고 싶습니다 (정확히 RESTTful). 그러나 REST는 CRUD와 유사하고 이벤트 소싱은 작업 기반이므로이를 함께 연결하지 못합니다. REST 서버에 대한 요청을 기반으로 명령 작성을 어떻게 설계 할 수 있는지 궁금합니다. 이 예제를 고려하십시오.

REST를 사용하면 File이라는 자원에 새로운 상태를 둘 수 있습니다. 한 번의 요청으로 새 파일 이름을 보낼 수 있으며, 상위 폴더 및 / 또는 파일 소유자 등을 변경할 수 있습니다.

이벤트 소싱을 사용할 수 있도록 서버를 구성하는 방법. 나는 이러한 가능성에 대해 생각하고 있었다 :

  1. 필드가 변경 된 서버에 결정하고 적절한 명령을 생성 ( RenameFileCommand, MoveFileCommand, ChangeOwnerCommand, ...)과 개별적으로 파견. 그러나이 설정에서 각 명령은 다른 명령을 트랜잭션에서 벗어나 자원에 대한 "원자"변경에서 벗어나지 못할 수 있습니다.

  2. 파견 단 하나 개의 명령 ( UpdateFileCommand)와 명령 핸들러에서, 더 정확하게 집계에서 변경 된 필드를 결정하고 대신 개별 이벤트를 전송 ( FileRenamedEvent, FileMovedEvent, OwnerChangedEvent, ...)

  3. 전혀 싫어하는 것 : 서버에 대한 요청에서 UI는 여전히 작업 기반이지만 (REST를 통해 통신이 이루어짐) 사용할 명령을 헤더에 지정합니다. 그러나 하나의 요청에서 하나의 필드 만 변경하도록 바인딩되지 않으므로 REST 통신의 다른 사용 (예 : 외부 앱)에서는 실패합니다. 또한 UI, REST 및 ES 기반 백엔드에 상당히 큰 결합을 가져옵니다.

어느 쪽을 선호하거나 이것을 처리하는 더 좋은 방법이 있습니까?

참고 사항 : 이벤트 소싱을 위해 Java 및 Axon Framework로 작성된 앱.


확실히 3 일은 아닙니다. 나는 1 일을 할 것이지만 그것에 대해 생각해야합니다.
inf3rno

단일 HTTP 요청이 여러 명령을 생성 할 수 있는지에 대한 질문이 있습니까? 잘 이해합니까? 임호. 그렇게 할 수는 있지만 한동안 DDD에 대해 읽지 않았으므로 이것을 구현하는 방법에 대한 샘플 코드를 확인해야합니다.
inf3rno

예를 찾았지만 CRUD입니다. github.com/szjani/predaddy-issuetracker-sample/blob/3.0/src/hu/… 저자에게 그의 의견이 무엇인지 물어볼 것입니다. 그는 저보다 DDD에 대해 더 많이 알고 있습니다.
inf3rno

1
즉각적인 일관성을 원한다면 "작업 단위"에서 여러 명령을 사용해야한다는 것입니다. 최종 일관성에 대해 이야기하고 있다면 그 질문은 이해가되지 않습니다. 원자 적으로 실행할 명령을 포함 할 수있는 CompositeCommand를 보내는 또 다른 솔루션입니다. 간단한 모음 일 수 있으며, 버스가 올바르게 처리 할 수있는 유일한 문제입니다.
inf3rno

1
그에 따르면 명령과 HTTP 요청 사이에 1 : 1 관계를 달성하려고 노력해야합니다. 당신이 그것을 할 수 없다면 (나쁜 냄새), 대량으로 (내가 합성이라고 함) 원자로 만들어야합니다.
inf3rno

답변:


11

여기에 사용자 프로세스와 구현이 일치하지 않을 수 있습니다.

첫째 : 사용자 가 파일을 동시에 여러 번 변경 하고 싶 습니까? 이름 변경 (경로 변경을 포함하거나 포함하지 않을 수 있음), 소유권 변경 및 파일 내용 변경 (논쟁을 위해)은 별도의 작업처럼 보입니다.

대답이 "예"인 경우를 생각해 보자. 사용자는 실제로 이러한 변경을 동시에하고 싶어한다.

-이 경우, 난 강력하게 여러 이벤트를 보내는 모든 구현에 대해 권하고 싶습니다 RenameFileCommand, MoveFileCommand, ChangeOwnerCommand이 표현하기 - 단일 사용자의 의도를.

왜? 이벤트가 실패 할 수 있기 때문입니다. 어쩌면 매우 드물지만 사용자가 원자처럼 보이는 작업을 제출했습니다. 다운 스트림 이벤트 중 하나가 실패하면 응용 프로그램 상태는 이제 유효하지 않습니다.

또한 각 이벤트 처리기간에 명확하게 공유되는 리소스에 대한 레이스 위험을 초대합니다. 파일 이름과 파일 경로가 중요하지 않은 방식으로 "ChangeOwnerCommand"를 작성해야합니다. 명령이 수신 될 때 오래되었을 수 있기 때문입니다.

파일 이동 및 이름 바꾸기로 이벤트가 아닌 편안한 시스템을 구현할 때는 eTag 시스템과 같은 것을 사용하여 일관성을 유지하는 것이 좋습니다. 편집중인 자원의 버전이 사용자가 마지막으로 검색 한 버전인지 확인하고 실패하면 실패합니다. 그 이후로 수정되었습니다. 그러나이 단일 사용자 조작에 대해 여러 명령을 발송하는 경우 각 명령 후에 자원 버전을 증가시켜야합니다. 따라서 사용자가 편집중인 자원이 실제로 읽은 자원과 동일한 버전인지 알 수있는 방법이 없습니다. .

내가 의미하는 바는 다른 사람이 거의 동시에 파일에 대해 다른 작업을 수행하는 경우입니다. 6 개의 명령은 어떤 순서로든 쌓을 수 있습니다. 원자 명령이 2 개 뿐인 경우 이전 명령이 성공하고 나중에 명령이 "마지막으로 검색된 이후 리소스가 수정되었습니다"라는 명령이 실패 할 수 있습니다. 그러나 명령이 원자 적이 지 않은 경우에는 이에 대한 보호가 없으므로 시스템 일관성이 위반됩니다.

흥미롭게도 2015 년 1 월 Thoughtworks 기술 레이더 에서 권장되는 REST ( Pest without PUT)라고하는 REST의 이벤트 기반 아키텍처와 같은 움직임이 있습니다. PUT이없는 Rest 에 대한 블로그는 여기 에 상당히 길다 .

기본적으로 POST, PUT, DELETE 및 GET은 소규모 응용 프로그램에는 적합하지만 다른 쪽 끝에 넣기 및 게시 및 삭제가 해석되는 방법을 가정하여 시작해야 할 경우 커플 링을 도입해야합니다. (예 : "은행 계좌와 연결된 리소스를 삭제할 때 계좌를 폐쇄해야합니다") 제안 된 솔루션은 REST를보다 이벤트 방식으로 처리하는 것입니다. 즉, 사용자 의도를 단일 이벤트 자원으로 POST 할 수 있습니다.

다른 경우는 더 간단합니다. 사용자가 모든 작업을 동시에 수행하지 않으려면 두지 마십시오. 각 사용자 의도에 대한 이벤트를 게시하십시오. 이제 리소스에서 etag 버전 관리를 사용할 수 있습니다.

리소스에 매우 다른 API를 사용하는 다른 응용 프로그램도 마찬가지입니다. 문제가납니다 RESTful API 위에 이전 API의 파사드를 구성하고 파사드를 가리킬 수 있습니까? 즉, REST 서버를 통해 파일에 여러 업데이트를 수행하는 서비스를 순서대로 노출합니까?

이전 솔루션 위에 RESTful 인터페이스를 구축하거나 REST 솔루션 위에 이전 인터페이스의 외관을 구축하지 않고 공유 데이터 리소스를 가리키는 두 API를 유지하려고 시도하면 심각한 문제가 발생합니다.


그러나 불일치를 볼 수 있지만 원칙적으로 REST는 새로운 상태를 리소스에 PUT하여 (일부 표현으로) 여러 필드의 상태를 업데이트 할 수 있습니다. 이것이 REST가 정의-표현 상태 전송에 의해 작동하는 방식이며, 단점은 프로세스에서 사용자의 의도가 손실된다는 것입니다. 또한 REST는 거의 외부 API의 표준입니다. 둘 다 사용할 수 있도록 앱을 디자인하고 싶습니다. ES로 인해 PUT을 제거하는 것은 해결 방법과 같습니다. 내가 볼 수 있듯이 명령 및 이벤트 처리가 하나의 트랜잭션에있는 한 여러 이벤트를 방출하는 하나의 업데이트 명령이 가능합니다.
빨간 머리

Etag는 어쨌든하고 싶은 일입니다. 또한 "더 긴 블로그 게시물"에 대한 링크는 첫 번째 링크와 동일합니다.
빨간 머리

PUT에 대한 더 많은 생각으로 약간의 고려를 한 후에 이것으로 돌아올 것입니다. 확실히 PUT을 제거하는 것은 "ES의 해결 방법"이 아닙니다. 블로그 링크를 수정했습니다.
완벽 주의자

4

지금은 다음 기사를 보았습니다.이 기사에서는 5 가지 수준의 미디어 유형을 따르는 동안 콘텐츠 유형 헤더의 서버에 대한 요청의 명령 이름을 지정하는 것이 좋습니다.

이 기사에서는 RPC 스타일이 REST에 적합하지 않다고 언급하고 Content-Type을 확장하여 명령 이름을 지정하도록 제안합니다.

일반적인 접근 방식 중 하나는 RPC 스타일 리소스를 사용하는 것입니다 (예 : / api / InventoryItem / {id} / rename). 이것은 임의의 동사에 대한 필요성을 제거하는 것처럼 보이지만 REST의 리소스 지향 프레젠테이션에 위배됩니다. 자원은 명사이고 HTTP 동사는 동사 / 조치이며 자체 설명 메시지 (REST의 원칙 중 하나)는 다른 축의 정보와 의도를 전달하는 수단이라는 점을 상기시켜야합니다. 실제로 HTTP 메시지 페이로드의 명령은 임의의 조치를 표현하기에 충분해야합니다. 그러나 메시지 본문에 의존하는 것은 본문이 일반적으로 스트림으로 전달되고 활동을 식별하기 전에 본문 전체를 버퍼링하는 것이 항상 가능하지 않고 현명하지 않기 때문에 자체 문제가 있습니다.

PUT /api/InventoryItem/4454c398-2fbb-4215-b986-fb7b54b62ac5 HTTP/1.1
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate,sdch
Content-Type:application/json;domain-model=RenameInventoryItemCommand`

기사는 여기에 있습니다 : http://www.infoq.com/articles/rest-api-on-cqrs

5 가지 레벨의 미디어 유형에 대한 자세한 내용은 여기를 참조하십시오. http://byterot.blogspot.co.uk/2012/12/5-levels-of-media-type-rest-csds.html


도메인 이벤트를 REST API에 노출하고 있지만, 나쁜 관행으로 생각하지만 CQRS 전용으로 새로운 "프로토콜"을 만들지 않기 때문에 솔루션이 마음에 듭니다. 헤더이며 RESTful 원칙을 준수합니다.

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