Làm thế nào để xử lý các tác dụng phụ trong CRQS khi phát lại các sự kiện?


10

Người ta nói rằng trong CQRS rất dễ sửa lỗi, bạn chỉ cần triển khai lại và sau đó phát lại các sự kiện.

Nhưng, điều gì sẽ xảy ra nếu một trong những sự kiện sẽ khiến một hệ thống bên ngoài không nằm trong tầm kiểm soát của bạn "vận chuyển một mặt hàng" cho khách hàng nếu bạn chỉ phát lại các sự kiện mà mặt hàng đó sẽ được vận chuyển hai lần.

Làm thế nào để bạn giải quyết điều đó?

Câu trả lời:


6

Bạn cần phân tách rõ ràng giữa các sự kiện sửa đổi trạng thái của mô hình đọc của bạn và các sự kiện (có khả năng) sửa đổi trạng thái của các hệ thống bên ngoài. Hãy chắc chắn rằng bạn không có bất kỳ "sự kiện hỗn hợp" nào sửa đổi cả hai trạng thái với nhau. Bằng cách đó, bạn có thể phát lại các sự kiện của mình trong một "chế độ phát lại" cụ thể trong đó các sự kiện đó cho hệ thống bên ngoài không được kích hoạt lại. Trong chế độ này, bạn cũng "mô phỏng" bất kỳ sự kiện nào ban đầu được bắt đầu bởi hệ thống bên ngoài (bây giờ bạn lấy chúng từ hàng đợi phát lại thay thế).

Đừng quên, bước triển khai lại có nghĩa là thực sự thiết lập lại trạng thái của mô hình đọc về thời điểm sớm hơn. Điều này có lẽ không có gì bạn có thể làm (hoặc cần làm) cho trạng thái của các hệ thống bên ngoài.


"Đừng quên, bước triển khai lại có nghĩa là thực sự thiết lập lại trạng thái của mô hình đọc về thời điểm sớm hơn. Đây có lẽ là điều bạn không thể làm (hoặc cần làm) đối với trạng thái của các hệ thống bên ngoài." -> nhưng nếu tôi muốn phát lại thử lại các cuộc gọi hệ thống bên ngoài không thành công như vận chuyển thì sao? trong trường hợp này, việc triển khai lại của tôi một phát lại sẽ không chỉ thiết lập lại trạng thái của mô hình đọc mà còn gây ra các sự kiện bên ngoài, điều này nghe có vẻ công bằng hay tôi đang thiếu một cái gì đó?
Jas

2
@Jas: bạn không muốn lạm dụng "phát lại" để thử lại một cuộc gọi hệ thống bên ngoài không thành công. Bạn sử dụng "phát lại" để có được mô hình đọc của hệ thống của chính bạn ở trạng thái như trước đây. Điều đó có nghĩa là trong trường hợp yêu cầu vận chuyển không thành công, hệ thống của bạn đã được thông báo trước về sự cố này và lưu trữ thông tin đó ở đâu đó trong trạng thái của nó. Phát lại đảm bảo thông tin này vẫn còn đó sau khi "triển khai lại & phát lại". Vì vậy, sau khi phát lại, hệ thống của bạn có thể áp dụng chiến lược "thử lại vận chuyển trong trường hợp thất bại" (không liên quan gì đến CQRS, bất kỳ hệ thống đặt hàng mạnh mẽ nào cũng cần có chiến lược như vậy).
Doc Brown

Thật thú vị, đây là những gì tôi có trong đầu để làm, chỉ tự hỏi liệu có "khuôn mẫu" nào về điều này không nên tôi không phát minh lại bánh xe!
Jas

3

Từ bài viết tìm nguồn cung ứng sự kiện của Martin Fowler :

Ý tưởng cơ bản của Tìm nguồn sự kiện là đảm bảo mọi thay đổi về trạng thái của ứng dụng được ghi lại trong một đối tượng sự kiện và các đối tượng sự kiện này được lưu trữ theo trình tự chúng được áp dụng trong cùng thời gian như chính trạng thái ứng dụng.

Vì vậy, khi bạn cần khôi phục trạng thái hệ thống của mình đến một thời điểm nhất định, bạn sẽ phát lại trạng thái được lưu trữ , chứ không phải xử lý sự kiện, cho đến thời điểm đó.

Điều đó đang được nói, nếu bạn chỉ làm việc với dữ liệu trạng thái, sẽ không có bất kỳ hiệu ứng nào trên hệ thống bên ngoài. Trừ khi bạn có trình kích hoạt hoặc người theo dõi trên cửa hàng sự kiện của mình, trong trường hợp đó bạn nên vô hiệu hóa chúng trong suốt thời gian khôi phục. Vì bạn nói rằng bạn không có quyền kiểm soát hệ thống bên ngoài, nên không có bất kỳ nỗ lực nào để khôi phục trạng thái của nó bằng cách sử dụng API được hiển thị vì bạn không biết tác dụng phụ nào có thể có trong hệ thống của họ. Nếu khôi phục đặt hệ thống ở trạng thái trung gian (ví dụ: do các hoạt động không thành công trong hệ thống bên ngoài) thì điều này không thuộc trách nhiệm của phát lại sự kiện.


2

Nhưng, điều gì sẽ xảy ra nếu một trong những sự kiện sẽ khiến một hệ thống bên ngoài không nằm trong tầm kiểm soát của bạn "vận chuyển một mặt hàng" cho khách hàng nếu bạn chỉ phát lại các sự kiện mà mặt hàng đó sẽ được vận chuyển hai lần.

Để chọn một ví dụ cụ thể, hãy xem xét cách tiếp cận "ít nhất một lần" đối với các tác dụng phụ có thể hoạt động.

State currentState = State.InitialState
for(Event e : events) {
    currentState = currentState.apply(e)
}
for(SideEffect s : currentState.querySideEffects()) {
    performSideEffect(s)

Vì vậy, mô hình miền theo dõi những gì cần phải được thực hiện; nhưng để lại thực tế cho ứng dụng

Trong bối cảnh chạy một lệnh, ý tưởng cơ bản trông giống nhau. Các tác dụng phụ thực tế xảy ra bên ngoài giao dịch cập nhật mô hình.

Vì vậy, các bài kiểm tra đơn vị cho mô hình của bạn có thể trông giống như

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced)

    // Then
    List.of(SendEmail) === currentState.applyAll(events).querySideEffects()
}

{
    // Given
    State currentState = State.InitialState

    // When
    Events events = List.of(OrderPlaced, EmailSent)

    // Then
    List.EMPTY === currentState.applyAll(events).querySideEffects()
}

Những điểm chính ở đây là

  • Cập nhật mô hình là tác dụng phụ miễn phí; các tác dụng phụ thực tế xảy ra bên ngoài giao dịch cập nhật mô hình.
  • Một sự kiện mô tả kết quả của tác dụng phụ cần phải quay lại.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.