Tại sao tách lớp CommandHandler với Xử lý () thay vì phương thức xử lý trong chính Lệnh


13

Tôi có một phần của mẫu CQRS được triển khai bằng S # arp Architecture như thế này:

public class MyCommand
{
    public CustomerId { get; set; }

    // some other fields
}

public class MyCommandHandler<MyCommand> : ICommandHandler<MyCommand, CommandResult>
{
    Handle(MyCommand command)
    {
        // some code for saving Customer entity

        return CommandResult.Success;
    }
}

Tôi tự hỏi tại sao không chỉ có lớp Commandchứa cả dữ liệu phương thức xử lý? Đây có phải là một loại lợi ích kiểm tra, trong đó bạn cần kiểm tra logic xử lý lệnh riêng biệt với các thuộc tính lệnh? Hoặc đó là một số yêu cầu kinh doanh thường xuyên, nơi bạn cần phải có một lệnh được xử lý bởi các triển khai khác nhau ICommandHandler<MyCommand, CommandResult>?


Tôi có cùng một câu hỏi, giá trị xem xét: blogs.cuttingedge.it/steven/posts/2011/...
rdhaundiyal

Câu trả lời:


14

Thật buồn cười, câu hỏi này chỉ khiến tôi nhớ lại chính xác cuộc trò chuyện tôi có với một trong những kỹ sư của chúng tôi về thư viện liên lạc mà tôi đang làm việc.

Thay vì các lệnh, tôi có các lớp Request và sau đó tôi có RequestHandlers. Thiết kế rất giống với những gì bạn đang mô tả. Tôi nghĩ một phần của sự nhầm lẫn mà bạn có là bạn thấy từ "lệnh" trong tiếng Anh và ngay lập tức nghĩ "động từ, hành động ... vv".

Nhưng trong thiết kế này, hãy nghĩ về Command (hoặc Request) như một chữ cái. Hoặc đối với những người không biết dịch vụ bưu chính là gì, hãy nghĩ e-mail. Nó chỉ đơn giản là nội dung, tách rời khỏi cách thức nội dung đó nên được hành động.

Tại sao bạn sẽ làm điều này? Trong hầu hết các trường hợp đơn giản, Mẫu lệnh không có lý do và bạn có thể yêu cầu lớp này thực hiện công việc trực tiếp. Tuy nhiên, thực hiện việc tách rời như trong thiết kế của bạn có ý nghĩa nếu hành động / lệnh / yêu cầu của bạn phải di chuyển một khoảng cách. Ví dụ: qua, ổ cắm hoặc đường ống, hoặc giữa tên miền và cơ sở hạ tầng. Hoặc có thể trong kiến ​​trúc của bạn, các lệnh của bạn cần phải được duy trì (ví dụ: trình xử lý lệnh có thể thực hiện 1 lệnh một lần, do một số sự kiện hệ thống, 200 lệnh đến và sau 40 quá trình đầu tiên bị tắt). Trong trường hợp đó, có một lớp chỉ có thông báo đơn giản, việc tuần tự hóa phần chỉ thành JSON / XML / binary / bất cứ thứ gì và chuyển nó xuống đường ống cho đến khi trình xử lý lệnh của nó sẵn sàng để xử lý nó.

Một ưu điểm khác của việc tách lệnh từ CommandHandler là bây giờ bạn có tùy chọn phân cấp kế thừa song song. Ví dụ, tất cả các lệnh của bạn có thể xuất phát từ một lớp lệnh cơ sở hỗ trợ tuần tự hóa. Và có thể bạn có 4 trong số 20 trình xử lý lệnh có nhiều điểm tương đồng, bây giờ bạn có thể lấy được chúng từ lớp cơ sở xử lý đã đến. Nếu bạn có dữ liệu và xử lý lệnh trong một lớp, loại mối quan hệ này sẽ nhanh chóng vượt khỏi tầm kiểm soát.

Một ví dụ khác cho việc tách rời sẽ là nếu lệnh của bạn yêu cầu rất ít đầu vào (ví dụ 2 số nguyên và một chuỗi) nhưng logic xử lý của nó đủ phức tạp để bạn muốn lưu trữ dữ liệu trong các biến thành viên trung gian. Nếu bạn xếp hàng 50 lệnh, bạn không muốn phân bổ bộ nhớ cho tất cả bộ lưu trữ trung gian đó, vì vậy bạn tách Command khỏi CommandHandler. Bây giờ bạn xếp hàng 50 cấu trúc dữ liệu trọng lượng nhẹ và lưu trữ dữ liệu phức tạp hơn chỉ được phân bổ một lần (hoặc N lần nếu bạn có N xử lý) bởi CommandHandler đang xử lý các lệnh.


Vấn đề là, trong bối cảnh này, lệnh / yêu cầu không phải là từ xa / vẫn tồn tại / vv .. Nó được xử lý trực tiếp. Và tôi không thể thấy việc tách hai người sẽ giúp kế thừa như thế nào. Nó thực sự sẽ làm cho nó khó hơn. Đoạn cuối cũng là loại bỏ lỡ. Tạo đối tượng không phải là hoạt động đắt tiền và 50 lệnh là số không thể bỏ qua.
Euphoric

@Euphoric: làm thế nào để bạn biết bối cảnh là gì? Trừ khi S # arp Architecture là một cái gì đó đặc biệt, tất cả những gì tôi thấy là một vài khai báo lớp và bạn không biết chúng được sử dụng như thế nào trong phần còn lại của ứng dụng. Nếu bạn không thích những con số tôi chọn như 50, hãy chọn thứ gì đó như 50 mỗi giây. Nếu không đủ, hãy chọn 1000 mỗi giây. Tôi chỉ cố gắng cung cấp các ví dụ. Hoặc bạn không nghĩ rằng trong bối cảnh này , anh ta sẽ có nhiều lệnh như vậy?
DXM

Ví dụ, cấu trúc chính xác được nhìn thấy ở đây weblogs.asp.net/shijuvarghese/archive/2011/10/18/ . Và không nơi nào nó nói những gì bạn nói. Và về tốc độ, vấn đề là bạn đã sử dụng đối số 'hiệu suất' mà không cần lược tả. Nếu bạn có yêu cầu cho thông lượng như vậy, bạn sẽ không sử dụng kiến ​​trúc chung chung mà xây dựng một cái gì đó chuyên biệt hơn.
Euphoric

1
Hãy cho tôi xem nếu đây là điểm cuối cùng của bạn: OP đã hỏi ví dụ và tôi nên nói, ví dụ, trước tiên bạn thiết kế cách đơn giản và ứng dụng của bạn hoạt động, sau đó bạn mở rộng và bạn mở rộng các vị trí nơi bạn sử dụng mẫu lệnh, sau đó bạn phát trực tiếp và bạn nhận được 10.000 máy nói chuyện với máy chủ của bạn và máy chủ của bạn vẫn sử dụng kiến ​​trúc ban đầu của bạn, sau đó bạn lập hồ sơ và xác định vấn đề, sau đó bạn có thể tách dữ liệu lệnh khỏi xử lý lệnh, nhưng chỉ sau khi bạn cấu hình. Nó sẽ thực sự làm bạn hạnh phúc hơn nếu tôi bao gồm tất cả những điều đó trong câu trả lời? Anh xin một ví dụ, tôi cho anh một cái.
DXM

... vì vậy tôi chỉ lướt qua bài đăng trên blog mà bạn đã đăng và nó dường như phù hợp với những gì tôi đã viết: tách chúng ra nếu lệnh của bạn phải di chuyển một khoảng cách. Trong blog, anh ta dường như đang đề cập đến một bus lệnh, về cơ bản chỉ là một đường ống, ổ cắm, hàng đợi tin nhắn, esb ... vv
DXM

2

Mẫu lệnh thông thường là về việc có dữ liệu và hành vi trong một lớp. Kiểu 'Mẫu / Trình xử lý' này hơi khác một chút. Ưu điểm duy nhất so với mẫu bình thường là thêm lợi thế của việc không có lệnh của bạn phụ thuộc vào các khung. Ví dụ, lệnh của bạn có thể cần truy cập DB để nó phải có một số loại bối cảnh hoặc phiên DB, nghĩa là nó phụ thuộc vào các khung công tác. Nhưng lệnh này có thể là một phần của miền của bạn, vì vậy bạn không muốn nó phụ thuộc vào các khung theo Nguyên tắc đảo ngược phụ thuộc . Tách các tham số đầu vào và đầu ra khỏi hành vi và có một số bộ điều phối để nối chúng có thể khắc phục điều này.

Mặt khác, bạn sẽ mất lợi thế về cả kế thừa và thành phần của các lệnh. Mà tôi nghĩ đó là sức mạnh thực sự.

Ngoài ra, nitlog nhỏ. Chỉ vì nó có Lệnh trong tên không biến nó thành một phần của CQRS. Đó là về một cái gì đó cơ bản hơn nhiều. Kiểu cấu trúc này có thể phục vụ cả dưới dạng lệnh và truy vấn ngay cả cùng một lúc.


Tôi đã thấy liên kết weblogs.asp.net/shijuvarghese/archive/2011/10/18/ nam bạn đã chỉ ra, nhưng tôi không thấy bất kỳ dấu hiệu nào của xe buýt trong mã S # arp Arch tôi có. Vì vậy, tôi đoán, sự tách biệt như vậy trong trường hợp của tôi chỉ lan truyền các lớp và bắn tung logic.
con nhỏ

1
@rgripper Sau đó, bạn đã không tìm kiếm đúng. github.com/sharparch
architecture / Shpp

Hmm, cảm ơn đã chỉ ra. Sau đó, trường hợp của tôi thậm chí còn tồi tệ hơn một chút, bởi vì trong mã tôi có ICommandProcessor là IOC'ed và được giải quyết thành CommandProcessor (chính nó đang tạo IOC cho trình xử lý lệnh) - một thành phần lầy lội. Và trong dự án dường như không có trường hợp kinh doanh nào cho nhiều hơn một hadler cho một lệnh.
rgripper
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.