2016-05-10 18 views
7

Tôi khá mới đối với các khái niệm thiết kế hướng miền và tôi đã gặp phải sự cố khi trả về các câu trả lời thích hợp trong API trong khi sử dụng một lệnh có các lệnh và trình xử lý lệnh cho logic miền.Làm thế nào để lấy lại dữ liệu từ một bus lệnh?

Giả sử chúng tôi đang xây dựng một ứng dụng có phương pháp thiết kế theo miền. Chúng tôi có một phần cuối và phần trước. Mặt sau có tất cả logic miền của chúng tôi với API được hiển thị. Giao diện người dùng sử dụng API để đưa ra yêu cầu cho ứng dụng.

Chúng tôi đang xây dựng logic miền của mình bằng các lệnh và trình xử lý lệnh được ánh xạ tới một bus lệnh. Dưới thư mục Domain của chúng ta, chúng ta có một lệnh để tạo một tài nguyên bài có tên là CreatePostCommand. Nó được ánh xạ tới trình xử lý CreatePostCommandHandler của nó thông qua bus lệnh.

final class CreatePostCommand 
{ 
    private $title; 
    private $content; 

    public function __construct(string $title, string $content) 
    { 
     $this->title = $title; 
     $this->content= $content; 

    } 

    public function getTitle() : string 
    { 
     return $this->title; 
    } 

    public function getContent() : string 
    { 
     return $this->content; 
    } 
} 

final class CreatePostCommandHandler 
{ 
    private $postRepository; 

    public function __construct(PostRepository $postRepository) 
    { 
     $this->postRepository = $postRepository; 
    } 

    public function handle(Command $command) 
    { 
     $post = new Post($command->getTitle(), $command->getContent()); 
     $this->postRepository->save($post); 
    } 
} 

Trong API của chúng tôi, chúng tôi có điểm cuối để tạo bài đăng. Điều này được định tuyến phương thức createPost trong một PostController trong thư mục Application của chúng ta.

final class PostController 
{ 
    private $commandBus; 

    public function __construct(CommandBus $commandBus) 
    { 
     $this->commandBus = $commandBus; 
    } 

    public function createPost($req, $resp) 
    { 
     $command = new CreatePostCommand($command->getTitle(), $command->getContent()); 
     $this->commandBus->handle($command); 

     // How do we get the data of our newly created post to the response here? 

     return $resp; 
    } 
} 

Bây giờ trong phương thức createPost, chúng tôi muốn trả lại dữ liệu của bài đăng mới tạo trong đối tượng phản hồi để ứng dụng đầu cuối của chúng tôi có thể biết về tài nguyên mới được tạo. Điều này là phiền hà vì chúng ta biết rằng theo định nghĩa, lệnh bus không trả về bất kỳ dữ liệu nào. Vì vậy, bây giờ chúng tôi đang bị mắc kẹt ở một vị trí khó hiểu, nơi chúng tôi không biết làm thế nào để thêm bài viết mới của chúng tôi để đối tượng phản ứng.

Tôi không chắc chắn làm thế nào để tiến hành với vấn đề này từ đây, một số câu hỏi tôi suy nghĩ:

  • Có một cách thanh lịch để trả lại dữ liệu của bài đăng trong phản ứng?
  • Tôi có đang triển khai không chính xác mẫu Command/CommandHandler/CommandBus không?
  • Đây có phải chỉ là trường hợp sử dụng sai cho mẫu Command/CommandHandler/CommandBus không?
+0

Có thể trùng lặp của [Điều gì sẽ được trả về từ API cho lệnh CQRS?] (Http://stackoverflow.com/questions/29916468/what-should-be-returned-from-the-api-for-cqrs- lệnh) – guillaume31

Trả lời

8

Đầu tiên, hãy chú ý rằng nếu chúng ta dây bộ điều khiển trực tiếp đến việc xử lý lệnh, chúng ta phải đối mặt với vấn đề tương tự:

public function createPost($req, $resp) 
    { 
     $command = new CreatePostCommand($command->getTitle(), $command->getContent()); 
     $this->createPostCommandHandler->handle($command); 

     // How do we get the data of our newly created post to the response here? 
     return $resp; 
    } 

Xe buýt đang giới thiệu một lớp về mình, cho phép bạn tách bộ điều khiển từ xử lý sự kiện, nhưng vấn đề bạn đang gặp phải là cơ bản hơn.

Tôi không chắc chắn làm thế nào để tiến hành với vấn đề này từ đây

TL; DR - nói với lĩnh vực gì định danh để sử dụng, chứ không phải là yêu cầu người miền gì nhận dạng sử dụng.

public function createPost($req, $resp) 
    { 
     // TADA 
     $command = new CreatePostCommand($req->getPostId() 
       , $command->getTitle(), $command->getContent()); 

     $this->createPostCommandHandler->handle($command); 

     // happy path: redirect the client to the correct url 
     $this->redirectTo($resp, $postId) 
    } 

Trong ngắn hạn, khách hàng, chứ không phải mô hình miền hoặc lớp kiên trì, chịu trách nhiệm tạo id của pháp nhân mới. Thành phần ứng dụng có thể đọc định danh trong chính lệnh đó và sử dụng nó để phối hợp chuyển tiếp trạng thái tiếp theo.

Ứng dụng, trong việc triển khai này, đơn giản là dịch thông điệp từ biểu diễn DTO sang biểu diễn miền.

An thực hiện thay thế sử dụng nhận dạng lệnh, và xuất phát từ lệnh đó bản sắc sẽ được sử dụng

 $command = new CreatePostCommand(
       $this->createPostId($req->getMessageId()) 
       , $command->getTitle(), $command->getContent()); 

Named UUIDs là một lựa chọn phổ biến trong các trường hợp sau; chúng là xác định và có xác suất va chạm nhỏ.

Bây giờ, câu trả lời đó là điều gì đó lừa gạt - chúng tôi thực sự chỉ chứng minh rằng chúng tôi không cần kết quả từ trình xử lý lệnh trong trường hợp này.

Nói chung, chúng tôi muốn có; Post/Redirect/Get là một thành ngữ tốt để sử dụng để cập nhật mô hình miền, nhưng khi khách hàng nhận được tài nguyên, chúng tôi muốn đảm bảo rằng họ đang nhận được phiên bản bao gồm các chỉnh sửa mà họ vừa tạo.

Nếu số lần đọc và ghi của bạn đang sử dụng cùng một bản ghi, đây không phải là vấn đề - bất cứ điều gì bạn đọc luôn là phiên bản mới nhất hiện có.

Tuy nhiên, là mẫu kiến ​​trúc phổ biến trong thiết kế điều khiển miền, trong trường hợp đó mô hình viết (xử lý bài đăng) sẽ chuyển hướng đến mô hình đã đọc - thường xuất bản dữ liệu cũ. Vì vậy, bạn có thể muốn bao gồm một phiên bản tối thiểu trong yêu cầu nhận, do đó trình xử lý biết làm mới bộ nhớ cache cũ của nó.

Có cách nào thanh lịch để trả lại dữ liệu của bài đăng trong phản hồi không?

Có một ví dụ trong mẫu mã mà bạn cung cấp với câu hỏi của bạn:

public function createPost($req, $resp) 

Hãy suy nghĩ về nó: $ req là một đại diện của sứ điệp yêu cầu http, đó là xấp xỉ tương tự như lệnh của bạn, và $ resp về bản chất là một xử lý cho một cấu trúc dữ liệu mà bạn có thể viết kết quả của bạn vào.

Nói cách khác, chuyển một cuộc gọi lại hoặc xử lý kết quả bằng lệnh của bạn và để trình xử lý lệnh điền vào chi tiết.

Tất nhiên, điều đó tùy thuộc vào cuộc gọi lại hỗ trợ xe buýt của bạn; không được bảo đảm.

Một khả năng khác, không yêu cầu thay đổi chữ ký của trình xử lý lệnh của bạn, là sắp xếp bộ điều khiển đăng ký các sự kiện được trình xử lý lệnh xuất bản. Bạn phối hợp một correlation id giữa lệnh và sự kiện và sử dụng điều đó để kéo lên sự kiện kết quả mà bạn cần.

Các chi tiết cụ thể không thành vấn đề rất nhiều - sự kiện được tạo ra khi xử lý các lệnh có thể được ghi vào một chiếc xe buýt thông điệp, hoặc sao chép vào một hộp thư, hoặc ....

+0

Mặc dù tôi đồng ý đó là một giải pháp có thể, tôi không hoàn toàn phù hợp với lý do bạn đưa ra. Tại sao bạn cần phải tạo một lệnh 'Create' idempotent? Nếu mục tiêu là để làm cho nó lặp lại, trong trường hợp nào bạn muốn lặp lại nó (với cùng một ID, đó là)? – guillaume31

+0

Ngoài ra, tôi không được bán trên sự khác biệt mà bạn tạo ra giữa Ứng dụng (Dịch vụ?) Và Trình xử lý Lệnh và các tác động của nó. Điều đó có nghĩa là Bộ điều khiển là Dịch vụ ứng dụng trong trường hợp của bạn? Bạn có đặt trình xử lý lệnh trong Miền không? – guillaume31

+0

@ guillaume31 Khi sử dụng xe buýt dịch vụ lâu bền, bạn muốn tính không tải trong bất kỳ trình xử lý tin nhắn nào để ngăn các thay đổi trùng lặp. Trình xử lý lệnh đó có vai trò của một dịch vụ ứng dụng trong DDD. – MikeSW

2

Tôi đang sử dụng phương pháp này và Tôi đang trả về kết quả lệnh. Tuy nhiên, đây là giải pháp hoạt động chỉ nếu trình xử lý lệnh là một phần của cùng một quá trình. Về cơ bản, tôi đang sử dụng một trình trung gian, bộ điều khiển và trình xử lý lệnh có một thể hiện của nó (thường là một sự phụ thuộc của hàm tạo).

Pseudo mã xử lý điều khiển

var cmd= new MyCommand(); 
var listener=mediator.GetListener(cmd.Id); 
bus.Send(cmd); 
//wait until we get a result or timeout 
var result=listener.Wait(); 
return result; 

Pseudo lệnh mã chức năng

var result= new CommandResult(); 
add some data here 
mediator.Add(result,cmd.Id); 

Đó là cách bạn có được thông tin phản hồi ngay lập tức. Tuy nhiên, điều này không nên được sử dụng để thực hiện một quy trình kinh doanh.

Btw, điều này không liên quan gì đến DDD, về cơ bản nó là một thông điệp hướng CQS có thể và nó được sử dụng trong một ứng dụng DDD.

+0

Không rõ lý do tại sao các trình xử lý lệnh cần phải là một phần của cùng một quá trình; nó trông giống như một trao đổi tin nhắn thẳng về phía trước, hoạt động trên các ranh giới quy trình. – VoiceOfUnreason

+0

@VoiceOfUnreason Bởi vì tất cả mọi thứ vượt qua ranh giới quá trình có tiềm năng để trở thành chạy dài và mọi thứ trở nên rất phức tạp. Đây là một giải pháp đơn giản hoạt động tốt với ràng buộc đó – MikeSW

Các vấn đề liên quan