Trong CQRS / ES, một lệnh có thể tạo một lệnh khác không?


10

Trong CQRS / ES, một lệnh được gửi từ máy khách đến máy chủ và được chuyển đến trình xử lý lệnh thích hợp. Trình xử lý lệnh đó tải một tổng hợp từ kho lưu trữ của nó và gọi một số phương thức trên nó và lưu nó trở lại kho lưu trữ. Sự kiện được tạo ra. Trình xử lý sự kiện / saga / trình quản lý quy trình có thể lắng nghe các sự kiện này để đưa ra các lệnh.

Vì vậy, các lệnh (đầu vào) tạo ra các sự kiện (đầu ra), sau đó có thể cung cấp lại cho hệ thống nhiều lệnh hơn (đầu vào). Bây giờ, có phải thông lệ cho một lệnh không phát ra bất kỳ sự kiện nào, mà là để tranh thủ một lệnh khác? Cách tiếp cận như vậy có thể được sử dụng để buộc thực thi trong một quy trình bên ngoài.

BIÊN TẬP:

Trường hợp sử dụng cụ thể mà tôi có trong tâm trí là việc xử lý các chi tiết thanh toán. Khách hàng gửi PayInvoicelệnh, có tải trọng bao gồm chi tiết thẻ tín dụng của người dùng. Lệnh PayInvoiceHandlerchuyển một MakeInvoicePaymentlệnh tới một quy trình riêng, chịu trách nhiệm tương tác với cổng thanh toán. Nếu thanh toán thành công, một InvoicePaidsự kiện được tạo ra. Nếu vì một lý do nào đó, hệ thống gặp sự cố sau khi PayInvoicelệnh được duy trì nhưng trước khi MakeInvoicePaymentlệnh được duy trì, chúng ta có thể theo dõi điều này bằng tay (không có khoản thanh toán nào được thực hiện). Nếu hệ thống gặp sự cố sau khi MakeInvoicePaymentlệnh vẫn tồn tại nhưng trướcInvoicePaidsự kiện vẫn tồn tại, chúng tôi có thể gặp tình huống thẻ tín dụng của người dùng bị tính phí nhưng hóa đơn không được gắn cờ là đã được thanh toán. Trong trường hợp đó, tình huống sẽ phải được điều tra thủ công và hóa đơn được đánh dấu thủ công là đã thanh toán.

Câu trả lời:


11

Nhìn lại, tôi nghĩ rằng tôi đã làm phức tạp vấn đề.

Nói chung, các lệnh nên ném một ngoại lệ hoặc đưa ra một hoặc nhiều sự kiện.

Nếu tôi có thể tóm tắt kiến ​​trúc của Tìm nguồn sự kiện thì nó sẽ như sau:

  • Các lệnh là đầu vào đại diện cho các hướng dẫn để làm một cái gì đó.
  • Sự kiện là kết quả đầu ra đại diện cho sự thật lịch sử của những gì đã được thực hiện.
  • Trình xử lý sự kiện có thể lắng nghe các sự kiện để đưa ra các lệnh, giúp phối hợp các phần khác nhau của hệ thống.

Có một lệnh tạo một lệnh khác gây ra sự mơ hồ trong ý nghĩa chung của các lệnh và sự kiện: nếu một lệnh "kích hoạt" một lệnh khác, điều đó có nghĩa là một lệnh là "sự thật lịch sử của những gì đã được thực hiện". Điều này mâu thuẫn với ý định của hai loại tin nhắn này và có thể trở thành một con đường trơn trượt, trong đó các nhà phát triển khác có thể kích hoạt các sự kiện từ các sự kiện, dẫn đến dữ liệu bị hỏng và sự không nhất quán cuối cùng .

Liên quan đến kịch bản cụ thể mà tôi đặt ra, yếu tố phức tạp là tôi không muốn luồng chính tương tác với cổng thanh toán, vì (là một quá trình xử lý đơn, liên tục), điều này sẽ không cho phép các lệnh khác được xử lý . Giải pháp đơn giản ở đây là sinh ra một luồng / quy trình khác để xử lý thanh toán.

Để minh họa, khách hàng gửi PayInvoicelệnh. Việc PayInvoiceHandlerbắt đầu một quy trình mới và vượt qua nó các chi tiết thanh toán. Quy trình mới giao tiếp với cổng thanh toán. Nếu thanh toán thành công, nó gọi invoice.markAsPaid()bằng số biên nhận (tạo ra InvoicePaidsự kiện). Nếu thanh toán không thành công, nó sẽ gọi invoice.paymentFailed()thông qua mã tham chiếu để điều tra thêm (tạo ra InvoicePaymentFailedsự kiện). Vì vậy, mặc dù thực tế là có một quá trình / luồng riêng biệt có liên quan, mẫu vẫn còn Command -> Event.

Liên quan đến thất bại, có ba kịch bản, hệ thống có thể sụp đổ sau khi PayInvoicelệnh được tồn nhưng trước khi InvoicePaidhoặc InvoicePaymentFailedsự kiện được tiếp tục tồn. Trong trường hợp này, chúng tôi không biết liệu thẻ tín dụng của người dùng có bị tính phí hay không. Trong trường hợp như vậy, người dùng sẽ nhận thấy khoản phí trên thẻ tín dụng của họ và khiếu nại, trong trường hợp đó, một thành viên của nhân viên có thể điều tra vấn đề và đánh dấu thủ công hóa đơn là đã thanh toán.


1
Cảm ơn rất nhiều cho cả câu hỏi và câu trả lời, món ăn ngon cho suy nghĩ. Nhìn chung, vẫn còn một vài điểm mà tôi thấy khó hiểu: (1) Tôi chưa thấy nhiều người nói về khái niệm xử lý sự kiện. (2) Giải thích của tôi về quan điểm của bạn chuyển phần lớn công việc sang các trình xử lý sự kiện, đến điểm mà trình xử lý lệnh trở thành một chức năng dịch thuần túy, đưa ra câu hỏi nếu việc tách thành các lệnh / sự kiện thậm chí là cần thiết. Xem câu hỏi tiếp theo này nếu bạn quan tâm.
bluenote10

3

Bạn sẽ có được một hệ thống được kết nối lỏng lẻo hơn về mặt kiến ​​trúc nếu bạn chỉ phát ra các sự kiện từ một lệnh. Nói cách khác, một lệnh không cần biết những lệnh bên ngoài khác sẽ phát hành; đó phải là trách nhiệm của bên ngoài (người nên đăng ký tham gia sự kiện và có thể, như bạn đã đề cập, người quản lý saga có trách nhiệm điều phối hoặc chỉ là một mô-đun khác có sự phụ thuộc vào các sự kiện đó).


2

Đề xuất xem: Udi Dahan trên Tin nhắn đáng tin cậy - nó không hoàn toàn như những gì bạn đang mô tả, nhưng có liên quan chặt chẽ.

Bây giờ, có phải thông lệ cho một lệnh không phát ra bất kỳ sự kiện nào, mà là để tranh thủ một lệnh khác?

Tôi chưa thấy ai giới thiệu thực hành đó.

Câu trả lời ngắn: nếu bạn không lưu một số trạng thái, thì bạn không thể khôi phục lệnh đã xử lý nếu bạn gặp sự cố sau khi thừa nhận rằng bạn đã nhận được nó.

Nếu bạn quyết định rằng bạn cần lưu trạng thái, không rõ ràng có bất kỳ lợi thế lớn nào để lên lịch cho lệnh thứ hai từ lệnh đầu tiên, thay vì sử dụng trình xử lý sự kiện.

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.