Tìm nguồn cung ứng sự kiện, một sự kiện, trạng thái của hai tập hợp đã thay đổi


10

Tôi đang cố gắng học cách DDD và các môn học liên quan. Tôi nảy ra một ý tưởng về bối cảnh giới hạn đơn giản để thực hiện "ngân hàng": có tài khoản, tiền có thể được gửi, rút ​​và chuyển giữa chúng. Nó cũng quan trọng để giữ lịch sử của những thay đổi.

Tôi đã xác định thực thể Tài khoản và việc tìm nguồn cung ứng sự kiện sẽ tốt để theo dõi các thay đổi trong đó. Các thực thể hoặc đối tượng giá trị khác không liên quan đến vấn đề, vì vậy tôi sẽ không đề cập đến chúng.

Khi xem xét tiền gửi và rút tiền - nó tương đối đơn giản, vì chỉ có một tổng hợp được sửa đổi.

Khi chuyển nó khác nhau - hai tổng hợp phải được sửa đổi bởi một sự kiện MoneyTransferred . DDD không chấp nhận sửa đổi nhiều tổng hợp trong một giao dịch. Mặt khác, quy tắc tìm nguồn cung ứng sự kiện là áp dụng các sự kiện cho các thực thể và sửa đổi trạng thái dựa trên chúng. Nếu sự kiện có thể được lưu trữ đơn giản trong cơ sở dữ liệu, sẽ không có vấn đề gì. Nhưng để ngăn chặn sửa đổi đồng thời các thực thể có nguồn gốc sự kiện, chúng ta phải triển khai một cái gì đó phiên bản luồng sự kiện của mỗi tổng hợp (để giữ giới hạn giao dịch của chúng). Với phiên bản xuất hiện một vấn đề khác - tôi không thể sử dụng các cấu trúc đơn giản để lưu trữ các sự kiện và đọc lại chúng để áp dụng chúng cho tổng hợp.

Câu hỏi của tôi là - làm thế nào tôi có thể tập hợp ba nguyên tắc đó: "một tổng hợp một giao dịch", "sự kiện-> thay đổi trong tổng hợp" và "phòng ngừa sửa đổi đồng thời"?

Câu trả lời:


7

Khi chuyển nó khác nhau - hai tổng hợp phải được sửa đổi bởi một sự kiện MoneyTransferred.

Chuyển tiền là một hành động riêng biệt từ việc cập nhật sổ cái.

MoneyTransferred
AccountCredited
AccountDebited

Bài tập cuối cùng đã phá vỡ sự lỏng lẻo này đối với tôi là nhận ra đó AccountOverdrawnlà một sự kiện, nó mô tả trạng thái của tài khoản mà không liên quan đến những người tham gia khác trong cuộc trao đổi này, do đó phải có lệnh chạy với tài khoản tạo ra nó.

Bạn không thể lấy được trạng thái một cách hợp lý như AccountOverdrawntừ mô hình đã đọc, bởi vì bạn không thể biết nếu bạn đã xem tất cả các sự kiện chưa - chỉ bản thân tổng hợp mới có một cái nhìn đầy đủ về lịch sử tại bất kỳ thời điểm nào.

Câu trả lời, tất nhiên, có ngay trong ngôn ngữ phổ biến - các tài khoản được ghi có hoặc ghi nợ để phản ánh nghĩa vụ của ngân hàng đối với khách hàng của mình.

Được rồi, nhưng điều đó có nghĩa là tôi cũng nên sử dụng các sự kiện AccountCredited và AccountDebited để gửi tiền và rút tiền, vì vậy tôi chỉ đăng ký không phải là nguyên nhân của thay đổi, mà là thay đổi do một số hành động khác gây ra. Nếu tôi muốn đảo ngược hành động tôi không thể, bởi vì không phải tất cả các sự kiện đều được đăng ký.

Tôi không hoàn toàn chắc chắn theo sau, bởi vì bạn có (đối với các trường hợp như thế này) một định danh tương quan tự nhiên, đó là chính id giao dịch.

Điều thứ hai - nó có nghĩa là tôi cần sử dụng một cái gì đó như saga.

Chính tả hơi khác nhau: bạn cần một cái gì đó giống như một con người đang gửi các lệnh đúng .

Có ít nhất hai cách bạn có thể làm điều đó. Một là sẽ có một thuê bao lắng nghe MoneyTransferredvà gửi hai lệnh đến sổ cái.

Một cách khác là theo dõi quá trình xử lý giao dịch dưới dạng tổng hợp riêng - hãy nghĩ về nó như một danh sách kiểm tra tất cả những việc cần thực hiện kể từ khi giao dịch xảy ra. Vì vậy, một MoneyTransferredtrình xử lý sự kiện sẽ gửi ProcessTransaction, lịch trình sẽ được hoàn thành và kiểm tra xem công việc nào đã được hoàn thành.


Được rồi, nhưng điều đó có nghĩa là tôi cũng nên sử dụng các sự kiện AccountCredited và AccountDebited để gửi tiền và rút tiền, vì vậy tôi chỉ đăng ký không phải là nguyên nhân của thay đổi, mà là thay đổi do một số hành động khác gây ra. Nếu tôi muốn đảo ngược hành động tôi không thể, bởi vì không phải tất cả các sự kiện đều được đăng ký. Làm thế nào tôi có thể làm điều này (nhân quả của các sự kiện)? Điều thứ hai - nó có nghĩa là tôi cần sử dụng một cái gì đó như saga. Làm thế nào nên là một mô hình chuyển giao sau đó? Một thời điểm tôi có phương thức chuyển khoản trên tài khoản. Khi được gọi, nó xuất bản sự kiện MoneyTransferred . Tôi không biết những gì nên bắt đầu một cái gì đó như saga.
cocsackie

Có phải vậy không -> AccountCreditedAccoundDebited rồi MoneyTransferred ? Giải pháp đầu tiên cập nhật cả hai tổng hợp trong một giao dịch (không bảo đảm tính nhất quán dưới bất kỳ hình thức nào)? Cũng không có tổng hợp nào có thể xuất bản MoneyTransferred -> không có mối tương quan. Giải pháp thứ hai có vẻ tốt hơn - ProcessTransaction có thể xuất bản MoneyTransferred và để tránh nhiều sửa đổi tổng hợp trong một giao dịch, tôi có thể xuất bản các sự kiện từ Tài khoản sau khi thực hiện giao dịch. Xin lỗi vì đã bị phạt. Thật khó hiểu cho người mới bắt đầu - không thể chỉ sử dụng một mẫu khác.
cocsackie

1

Một chi tiết quan trọng trong việc hiểu các tài khoản dựa trên giao dịch: balancethuộc tính accountthực sự là một trường hợp không chuẩn hóa. Đó là để thuận tiện. Trong thực tế, số dư của một tài khoản là tổng số giao dịch của tài khoản đó và bạn không thực sự cần tài khoản đó để có số dư.

Ghi nhớ điều này, hành động chuyển tiền không nên cập nhật accountmà là chèn vào transaction.

Điều đó đang được nói, có một quy tắc quan trọng khác: hành động thêm một transactionnên là nguyên tử với một bản cập nhật cho (trường cân bằng không chuẩn hóa) account.

Bây giờ nếu tôi hiểu khái niệm DDD của tổng hợp, thì những điều sau đây có vẻ phù hợp:

Tổng hợp là một ranh giới logic cho những thứ có thể thay đổi trong giao dịch kinh doanh của một bối cảnh nhất định. Một tập hợp có thể được đại diện bởi một lớp duy nhất hoặc vô số các lớp. Nếu có nhiều hơn một lớp cấu thành một tập hợp thì một trong số chúng là lớp thực thể hay còn gọi là lớp gốc. Tất cả quyền truy cập vào tổng hợp từ bên ngoài phải xảy ra thông qua lớp gốc.

Vì vậy, về mặt thiết kế DDD tôi sẽ đề xuất:

  1. Có một tổng hợp để đại diện cho việc chuyển nhượng

  2. Tập hợp bao gồm các đối tượng sau: chuyển (đối tượng gốc); đối tượng gốc được liên kết với hai danh sách giao dịch (một cho mỗi tài khoản); và mỗi danh sách giao dịch được liên kết với một tài khoản.

  3. Tất cả quyền truy cập vào chuyển nên được thiền định bởi đối tượng gốc (the transfer).

Nếu bạn đang cố gắng thực hiện hỗ trợ chuyển không đồng bộ, thì mã chính của bạn chỉ cần lo lắng về việc tạo chuyển khoản, trong trạng thái "đang chờ xử lý". Bạn có thể có một chủ đề khác hoặc một công việc thực sự chuyển tiền (chèn vào lịch sử giao dịch và do đó cập nhật số dư) và đặt chuyển khoản thành "đã đăng".

Nếu bạn đang tìm cách thực hiện giao dịch chuyển khoản theo thời gian thực, thì logic nghiệp vụ sẽ tạo một transfervà đối tượng đó sẽ điều phối các hoạt động khác trong thời gian thực.

Về mặt ngăn ngừa các vấn đề tương tranh, thứ tự đầu tiên của doanh nghiệp là chèn giao dịch ghi nợ vào danh sách giao dịch cho tài khoản nguồn (tất nhiên là cập nhật số dư). Điều này sẽ phải được thực hiện nguyên tử ở cấp cơ sở dữ liệu (thông qua một thủ tục được lưu trữ). Sau khi ghi nợ xảy ra, phần còn lại của chuyển khoản sẽ có thể thành công bất kể các vấn đề tương tranh, vì không nên có bất kỳ quy tắc kinh doanh nào ngăn chặn tín dụng vào tài khoản mục tiêu.

(Trong thế giới thực, tài khoản ngân hàng có khái niệm về một bản ghi nhớ hỗ trợ khái niệm về một cam kết hai giai đoạn lười biếng. Tạo bài đăng ghi nhớ rất nhẹ và dễ dàng, và nó cũng có thể được khôi phục mà không gặp sự cố. bài viết ghi nhớ đến một bài viết khó là khi tiền thực sự di chuyển-- điều này không thể quay trở lại-- và thể hiện giai đoạn thứ hai của cam kết hai pha, chỉ xảy ra sau khi tất cả các quy tắc xác thực đã được kiểm tra).


0

Tôi hiện cũng đang ở giai đoạn học tập. Từ quan điểm thực hiện, đây là cách tôi cảm thấy bạn sẽ thực hiện hành động này.

Công văn TransferMoneyCommand sẽ tăng các sự kiện sau [MoneyTransferEvent, AccountDebitedEvent]

Lưu ý trước khi nó làm tăng các sự kiện này, xác thực lệnh hời hợt và xác thực logic miền sẽ cần được thực hiện, tức là tài khoản có đủ số dư không?

Kiên trì các sự kiện (với phiên bản) để đảm bảo không có vấn đề nhất quán. Lưu ý rằng có thể có một lệnh đồng thời khác (như rút toàn bộ tiền) đã quản lý để thành công và lưu các sự kiện trước lệnh này, vì vậy trạng thái hiện tại của tổng hợp có thể đã hết hạn và do đó các sự kiện được nêu lên ở trạng thái cũ và không chính xác. Nếu việc lưu các sự kiện thất bại, bạn sẽ cần thử lại lệnh từ đầu.

Khi các sự kiện được lưu thành công trong cơ sở dữ liệu, bạn có thể xuất bản hai sự kiện được nêu ra.

AccountDebitedEvent sẽ xóa tiền khỏi tài khoản của người trả tiền (cập nhật trạng thái tổng hợp và mọi mô hình xem / chiếu liên quan)

MoneyTransferEvent khởi động Saga / Trình quản lý quy trình.

Công việc của saga / người quản lý quy trình sẽ là cố gắng ghi có vào tài khoản của người được trả tiền, nếu thất bại, họ sẽ cần ghi có số dư lại cho người trả tiền.

Người quản lý Saga / Process sẽ xuất bản một CreditAccountCommand được áp dụng cho tài khoản của người nhận và nếu thành công hơn AccountCreditedEvent sẽ được nâng lên.

Từ quan điểm tìm nguồn cung ứng sự kiện, nếu bạn muốn đảo ngược hành động này, tất cả các sự kiện trong giao dịch này sẽ có id tương quan / nguyên nhân là TransferMoneyCommand ban đầu mà bạn có thể sử dụng để nâng cao sự kiện cho các hoạt động hoàn tác / đảo ngược.

Hãy đề nghị bất kỳ vấn đề hoặc cải tiến tiềm năng ở trê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.