Tìm nguồn cung ứng sự kiện và REST


17

Tôi đã xem qua thiết kế Tìm nguồn sự kiện và tôi muốn sử dụng trong một ứng dụng cần có ứng dụng khách REST (chính xác là RESTful). Tuy nhiên tôi không kết nối chúng với nhau vì REST khá giống CRUD và tìm nguồn cung ứng sự kiện dựa trên nhiệm vụ. Tôi đã tự hỏi làm thế nào bạn có thể thiết kế việc tạo các lệnh dựa trên các yêu cầu đến máy chủ REST. Xem xét ví dụ này:

Với REST, bạn có thể đặt trạng thái mới cho tài nguyên có tên là File. Trong một yêu cầu bạn có thể gửi tên tệp mới, bạn có thể thay đổi thư mục mẹ và / hoặc thay đổi chủ sở hữu của tệp, v.v.

Làm thế nào để xây dựng máy chủ để tôi có thể sử dụng nguồn sự kiện. Tôi đã suy nghĩ về những khả năng này:

  1. Xác định trên máy chủ mà các lĩnh vực đã được thay đổi và tạo ra các lệnh thích hợp ( RenameFileCommand, MoveFileCommand, ChangeOwnerCommand, ...) và cử những cá nhân. Tuy nhiên, trong thiết lập này, mỗi lệnh có thể không khiến người khác rời khỏi giao dịch và do đó thay đổi "nguyên tử" đối với tài nguyên.

  2. Công văn chỉ có một lệnh ( UpdateFileCommand) và trong xử lý lệnh, chính xác hơn trong tổng hợp, xác định các lĩnh vực đã được thay đổi và gửi các sự kiện cá nhân thay vì ( FileRenamedEvent, FileMovedEvent, OwnerChangedEvent, ...)

  3. Cái này tôi không thích chút nào: Trong yêu cầu đến máy chủ tôi sẽ chỉ định trong các tiêu đề nên sử dụng lệnh nào, vì UI vẫn dựa trên nhiệm vụ (nhưng giao tiếp được thực hiện thông qua REST). Tuy nhiên, nó sẽ thất bại trong bất kỳ việc sử dụng giao tiếp REST nào khác (ví dụ: trong các ứng dụng bên ngoài) vì chúng không bị ràng buộc chỉ thay đổi một trường trong một yêu cầu. Ngoài ra, tôi mang một khớp nối khá lớn vào giao diện người dùng dựa trên UI, REST và ES.

Cái nào bạn thích hoặc có cách nào tốt hơn để xử lý việc này?

Lưu ý bên lề: ứng dụng được viết bằng Java và Axon Framework để tìm nguồn cung ứng sự kiện.


Chắc chắn không phải là thứ 3. Tôi sẽ làm thứ 1, nhưng tôi phải suy nghĩ về nó.
inf3rno

Bạn có thắc mắc về việc một yêu cầu HTTP có thể dẫn đến nhiều Lệnh không? Tôi có hiểu rõ không? Imho. nó sẽ có thể làm như vậy, nhưng tôi đã không đọc về DDD trong một thời gian, vì vậy tôi phải kiểm tra một mã mẫu về cách thực hiện điều này.
inf3rno

Tôi đã tìm thấy một ví dụ, nhưng đó là CRUD. github.com/szjani/predaddy-issuetracker-sample/blob/3.0/src/hu/... tôi sẽ hỏi các tác giả những gì là ý kiến của mình, anh ta biết thêm về DDD hơn tôi.
inf3rno

1
Của tôi là bạn nên sử dụng nhiều lệnh trong một "đơn vị công việc" nếu bạn muốn sự nhất quán ngay lập tức. Nếu bạn đang nói về tính nhất quán cuối cùng thì câu hỏi không có ý nghĩa với tôi. Một giải pháp khả thi khác để gửi một CompositeCommand có thể chứa các Lệnh bạn muốn thực thi nguyên tử. Nó có thể là một bộ sưu tập đơn giản, điều duy nhất quan trọng là xe buýt có thể xử lý nó đúng cách.
inf3rno

1
Theo ông, bạn nên cố gắng đạt được mối quan hệ 1: 1 giữa các lệnh và yêu cầu HTTP. Nếu bạn không thể làm điều đó (đó là một mùi hôi), thì bạn nên sử dụng số lượng lớn (tôi gọi nó là hỗn hợp) để biến nó thành nguyên tử.
inf3rno

Câu trả lời:


11

Tôi nghĩ rằng bạn có thể có một quy trình người dùng để thực hiện không khớp ở đây.

Đầu tiên: người dùng có thực sự muốn thực hiện đồng thời nhiều thay đổi cho một tệp không? Việc đổi tên (có thể bao gồm hoặc không bao gồm thay đổi đường dẫn?), Thay đổi quyền sở hữu và có thể thay đổi nội dung tệp (vì lý do tranh luận) có vẻ như là các hành động riêng biệt.

Hãy xem trường hợp câu trả lời là "có" - người dùng của bạn thực sự muốn thực hiện những thay đổi này cùng một lúc.

Trong trường hợp đó, tôi mạnh mẽ khuyên bạn chống lại bất kỳ thực hiện mà gửi nhiều sự kiện - RenameFileCommand, MoveFileCommand, ChangeOwnerCommand- đại diện này đơn mục đích sử dụng.

Tại sao? Bởi vì các sự kiện có thể thất bại. Có thể nó cực kỳ hiếm, nhưng người dùng của bạn đã gửi một hoạt động trông có vẻ nguyên tử - nếu một trong những sự kiện xuôi dòng không thành công, thì trạng thái ứng dụng của bạn hiện không hợp lệ.

Bạn cũng đang mời các mối nguy hiểm chủng tộc trên một tài nguyên được chia sẻ rõ ràng giữa mỗi người xử lý sự kiện. Bạn sẽ cần phải viết "ChangeOwnerCommand" theo cách mà tên tệp và đường dẫn tệp không quan trọng, vì chúng có thể bị lỗi thời trước khi nhận được lệnh.

Khi triển khai một hệ thống nghỉ ngơi không do sự kiện điều khiển với việc di chuyển và đổi tên tệp, tôi thích đảm bảo tính nhất quán bằng cách sử dụng một cái gì đó giống như hệ thống eTag - đảm bảo rằng phiên bản của tài nguyên đang được chỉnh sửa là phiên bản mà người dùng truy xuất lần cuối và thất bại nếu nó đã được sửa đổi kể từ đó. Nhưng nếu bạn đang gửi nhiều lệnh cho thao tác người dùng này, bạn sẽ cần tăng phiên bản tài nguyên của mình sau mỗi lệnh - vì vậy bạn không có cách nào để biết rằng tài nguyên mà người dùng đang chỉnh sửa thực sự là phiên bản giống như tài nguyên họ đọc lần cuối .

Điều tôi muốn nói là - nếu người khác thực hiện một thao tác khác trên tệp gần như cùng một lúc. 6 lệnh có thể xếp chồng lên nhau theo bất kỳ thứ tự nào. Nếu chúng ta chỉ có 2 lệnh nguyên tử, lệnh trước đó có thể thành công và lệnh sau có thể thất bại "tài nguyên đã bị sửa đổi kể từ lần lấy cuối cùng". Nhưng không có sự bảo vệ chống lại điều này khi các lệnh không phải là nguyên tử, do đó tính nhất quán của hệ thống bị vi phạm.

Điều thú vị là có một phong trào hướng tới một cái gì đó giống như kiến ​​trúc dựa trên sự kiện trong REST, được gọi là "Nghỉ ngơi mà không cần PUT", được đề xuất trong radar công nghệ Th Thinkworks, tháng 1 năm 2015 . Có một blog dài hơn đáng kể về Nghỉ ngơi mà không cần PUT ở đây .

Về cơ bản, ý tưởng là POST, PUT, DELETE và GET đều tốt cho các ứng dụng nhỏ, nhưng khi bạn cần bắt đầu giả định cách đặt và đăng và xóa có thể được diễn giải ở đầu kia, bạn giới thiệu khớp nối. (ví dụ: "khi tôi XÓA tài nguyên được liên kết với tài khoản ngân hàng của mình, tài khoản sẽ bị đóng") Và giải pháp được đề xuất là xử lý REST theo cách có nguồn gốc Sự kiện hơn. tức là cho phép POST mục đích của người dùng dưới dạng một tài nguyên sự kiện.

Các trường hợp khác là đơn giản hơn. Nếu người dùng của bạn không muốn thực hiện tất cả các hoạt động đó cùng một lúc, đừng để họ. POST một sự kiện cho mỗi ý định người dùng. Bây giờ bạn có thể sử dụng phiên bản etag trên tài nguyên của bạn.

Đối với các ứng dụng khác đang sử dụng API rất khác với tài nguyên của bạn. Điều đó có mùi như rắc rối. Bạn có thể xây dựng một mặt tiền của API cũ trên đầu API RESTful của bạn và chỉ chúng ở mặt tiền không? tức là phơi bày một dịch vụ thực hiện nhiều cập nhật cho một tệp theo trình tự thông qua máy chủ REST?

Nếu bạn không xây dựng giao diện RESTful trên đầu giải pháp cũ, cũng không xây dựng mặt tiền của giao diện cũ trên đầu giải pháp REST và cố gắng duy trì cả hai API trỏ vào tài nguyên dữ liệu dùng chung, bạn sẽ gặp phải những vấn đề đau đầu.


Tôi có thể thấy sự không phù hợp, tuy nhiên, theo nguyên tắc REST, có thể cập nhật trạng thái của nhiều trường bằng cách PUTting trạng thái mới thành tài nguyên (theo một số biểu diễn). Đây là cách REST hoạt động theo định nghĩa - chuyển trạng thái đại diện, mặt trái là ý định của người dùng bị mất trong quá trình. Ngoài ra, REST gần như là một tiêu chuẩn cho API bên ngoài. Tôi chỉ muốn thiết kế ứng dụng để tôi có thể sử dụng cả hai. Loại bỏ PUT giống như cách giải quyết vì ES. Từ những gì tôi có thể thấy, một lệnh cập nhật phát ra nhiều sự kiện là có thể thực hiện được miễn là lệnh và xử lý sự kiện nằm trong một giao dịch.
tóc đỏ

Etag là một cái gì đó tôi muốn làm cách nào. Ngoài ra liên kết của bạn đến "bài blog dài hơn" cũng giống như liên kết đầu tiên.
tóc đỏ

Tôi sẽ trở lại vấn đề này sau khi xem xét với nhiều suy nghĩ hơn PUT. Chắc chắn, loại bỏ PUT không chỉ là "cách giải quyết cho ES". Tôi đã sửa liên kết blog.
cầu toàn

4

Ngay bây giờ tôi đã chạy vào bài viết sau, trong đó khuyến khích chỉ định tên lệnh trong yêu cầu đến máy chủ trong tiêu đề Kiểu nội dung (trong khi theo 5 cấp loại phương tiện).

Trong bài viết, họ đề cập đến kiểu RPC là xấu cho REST và đề nghị mở rộng Kiểu nội dung để chỉ định tên lệnh:

Một cách tiếp cận phổ biến là sử dụng tài nguyên theo kiểu RPC ví dụ / api / InventoryItem / {id} / đổi tên. Trong khi điều này dường như loại bỏ sự cần thiết của các động từ tùy ý, nó lại chống lại cách trình bày theo định hướng tài nguyên của REST. Chúng ta cần được nhắc nhở rằng một tài nguyên là một danh từ và động từ HTTP là động từ / hành động và thông điệp tự mô tả (một trong những nguyên lý của REST) ​​là phương tiện để truyền đạt các trục thông tin và ý định khác. Trong thực tế, lệnh trong tải trọng của thông điệp HTTP phải đủ để thể hiện bất kỳ hành động tùy ý nào. Tuy nhiên, việc dựa vào cơ thể của thông điệp có vấn đề của riêng nó vì cơ thể thường được phân phối dưới dạng luồng và đệm toàn bộ cơ thể trước khi xác định hành động không phải lúc nào cũng có thể và cũng không khôn ngoan.

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`

Bài viết ở đây: http://www.infoq.com/articles/rest-api-on-cqrs

Bạn có thể đọc thêm về 5 cấp loại phương tiện tại đây: http://byterot.blogspot.co.uk/2012/12/5-levels-of-media-type-rest-csds.html


Mặc dù họ đang phơi bày các sự kiện miền cho API REST, mà tôi sẽ coi là một thực tiễn xấu, tôi thích giải pháp này vì nó không tạo ra một "giao thức" mới chỉ dành cho CQRS, có thể là gửi tên lệnh trong phần thân hoặc thêm vào tiêu đề và vẫn đúng với các nguyên tắc RESTful.

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.