2015-06-17 15 views
5

Tôi đang sử dụng CQRS + ES và tôi có vấn đề về mô hình hóa không thể tìm thấy giải pháp cho.
Bạn có thể bỏ qua bên dưới và trả lời câu hỏi chung trong tiêu đề: Bạn sẽ truy vấn dữ liệu cần thiết cho logic kinh doanh ở đâu?
Xin lỗi vì nó trở thành một câu hỏi phức tạp, tâm trí của tôi bị xoắn vào lúc này !!!
Đây là vấn đề:
Tôi có người dùng là thành viên của nhóm. Đó là một mối quan hệ nhiều đến nhiều. Mỗi người dùng có trạng thái sẵn có cho mỗi nhóm.
Các đội nhận được vé, mỗi đội có một hệ số tải nhất định, nên được chỉ định cho một trong các thành viên của nhóm tùy thuộc vào tình trạng sẵn có và tổng tải trọng của họ.
Vấn đề đầu tiên, Tôi cần truy vấn danh sách người dùng có sẵn trong nhóm và chọn người có ít tải nhất vì anh ấy đủ điều kiện cho bài tập. (cần lưu ý rằng đây là một trong những trường hợp, nó có thể là một truy vấn khác nhau để chạy)
Second Issue, hệ số tải của một vé có thể thay đổi vì vậy tôi phải đi mà vào xem xét khi tính tổng tải mỗi người dùng . Lưu ý rằng mặc dù vé có thể thuộc về 1 nhóm, nhiệm vụ phải dựa trên tổng tải của người dùng chứ không phải tải của anh ấy cho mỗi đội đó.
Hiện tại, TicketReceivedEvent được nhận bởi ngữ cảnh giới hạn này và tôi nên kích hoạt luồng công việc để gán vé đó cho người dùng.
Giải pháp có thể:
CQRS + ES - Nơi truy vấn Dữ liệu cần thiết cho logic nghiệp vụ?

  1. Cách đơn giản nhất sẽ được xếp hàng tuần tự các sự kiện và gửi một AssignTicketToUser lệnh và có một truy vấn dịch vụ mô hình đọc cho user id, có người sử dụng và user.assignTicket (Ticket). Khi TicketAssignedEvent được nhận, hãy gửi lệnh gán tiếp theo. Nhưng nó có vẻ là một lá cờ đỏ để truy vấn mô hình đọc từ bên trong trình xử lý lệnh! và một rắc rối để xếp hàng tất cả các vé này!
  2. Có một người quản lý quy trình cho mỗi người dùng với tính khả dụng/nhóm và vé được chỉ định cho người dùng đó. Trong trường hợp đó, chúng tôi thay thế truy vấn sang bên đọc bằng truy vấn "quản lý quá trình tra cứu" và trình xử lý lệnh sẽ gọi Ticket.AssignTo (Người dùng). Con là tôi nghĩ quá nhiều logic kinh doanh bị rò rỉ bên ngoài mô hình miền cụ thể là chúng tôi đang kéo tất cả thông tin/mô hình từ Tổng hợp người dùng để làm cho nó có sẵn để truy vấn

Tôi có xu hướng đi với giải pháp đầu tiên, nó có vẻ dễ dàng hơn để duy trì, sửa đổi/mở rộng và xác định vị trí trong mã nhưng có thể có một cái gì đó tôi đang mất tích.

Trả lời

0

Luôn luôn (tốt, 99,99% các trường hợp) trong lớp doanh nghiệp/tên miền, tức là trong phần "Lệnh" của CQRS. Điều này có nghĩa là các kho lưu trữ của bạn nên có các phương thức cho các truy vấn cụ thể và mô hình kiên trì của bạn phải đủ khả năng truy vấn cho mục đích này. Điều này có nghĩa là bạn phải biết thêm về các trường hợp sử dụng Miền của bạn trước khi quyết định cách triển khai tính kiên trì.

Sử dụng tài liệu db (mongodb, db quạ hoặc postgres) có thể làm cho công việc dễ dàng hơn. Nếu bạn đang mắc kẹt với một rdbms hoặc một cửa hàng giá trị khóa, tạo các bảng truy vấn, tức là một mô hình đã đọc cho mô hình ghi, hoạt động như một chỉ mục :) (điều này giả sử bạn đang sắp xếp các đối tượng). Nếu bạn đang lưu trữ những thứ có liên quan với lược đồ bảng cụ thể cho từng loại thực thể (chi phí khổng lồ, bạn đang làm phức tạp cuộc sống của bạn) thì thông tin có thể dễ dàng truy vấn được.

+0

Thx cho câu trả lời, hiện tại tôi đang sử dụng một db mssql và mô hình hóa cửa hàng sự kiện trong đó. Im serializing sự kiện và lưu chúng. Chỉ để chắc chắn rằng tôi hiểu chính xác, bạn đang nói tôi nên truy vấn phía đọc từ trình xử lý lệnh. – hsen

+0

Không, tôi nói, bạn nên có một số chỉ mục truy vấn được sử dụng để xác định các thực thể kinh doanh theo một tiêu chí kinh doanh. Nó vẫn là một phần của mô hình 'lệnh' – MikeSW

+0

Điều này rơi vào tùy chọn thứ hai tôi đoán. Tôi đã lưu các tiêu chí cần thiết trong trình quản lý quy trình của mình và sau đó truy vấn được thực hiện bởi bộ định tuyến để định vị trình quản lý quy trình/saga phù hợp để xử lý TicketCreatedEvent. – hsen

-1

Bạn có thể truy vấn dữ liệu bạn cần trong dịch vụ ứng dụng của mình. Điều này có vẻ tương tự như giải pháp đầu tiên của bạn.

Thông thường, bạn giữ tập hợp tham chiếu chéo của mình, vì vậy tôi không hoàn toàn chắc chắn về vấn đề đầu tiên xuất phát từ đâu. Mỗi người dùng phải có danh sách các đội thuộc về và mỗi nhóm có danh sách người dùng. Bạn có thể bổ sung dữ liệu này với bất kỳ thuộc tính nào bạn muốn, bao gồm, ví dụ, tính khả dụng. Vì vậy, khi bạn đọc tổng hợp của mình, bạn có sẵn dữ liệu trực tiếp. Chắc chắn, bạn sẽ có rất nhiều dữ liệu trùng lặp, nhưng điều này là rất phổ biến.

Trong mô hình sự kiện nguồn không bao giờ kho lưu trữ miền có thể cung cấp khả năng truy vấn bất kỳ. AggregateSource bởi Yves Reynhout là một tài liệu tham khảo tốt, đây là IRepository interface ở đó. Bạn có thể dễ dàng thấy không có phương thức "Truy vấn" trong giao diện này.

Ngoài ra còn có một câu hỏi tương tự Domain queries in CQRS

+0

-1 Bạn đang tạo ra một mớ hỗn độn trong các khái niệm. Trình xử lý lệnh không nên có quyền truy cập vào một mô hình đọc, đơn giản vì mô hình đọc không phải là mối quan tâm của nó và mô hình đọc có thể được tạo ra _async_ do đó trình xử lý sẽ làm việc với một mô hình không nhất quán. Một tổng hợp gốc không truy vấn bất cứ điều gì, kho lưu trữ nào. ES có nghĩa là biểu diễn trạng thái đối tượng dưới dạng luồng sự kiện, đó là một chi tiết _implementation_, nó không có bất kỳ câu nói nào trong việc xác định giao diện của một kho lưu trữ. Cuối cùng, bạn đang bối rối truy vấn UI/báo cáo với Truy vấn miền, đây là một vấn đề khác. – MikeSW

+0

Tôi không viết các truy vấn tổng hợp đó là mô hình đã đọc. Vì bạn rất chắc chắn, vui lòng chỉ cho tôi một kho lưu trữ có truy vấn tại https://github.com/gregoryyoung/m-r/tree/master/SimpleCQRS. Câu hỏi là về CQRS + ES. Làm thế nào mà chúng ta đang trừu tượng hóa từ * chi tiết triển khai * nếu điều này trực tiếp trong câu hỏi được hỏi ??? Trong một sự kiện có nguồn gốc viết mô hình, không có truy vấn có thể, đây là một tuyên bố rõ ràng. –

+0

Tôi đồng ý về việc truy vấn từ trình xử lý lệnh. Đây là lỗi của tôi, tôi đã thay đổi câu trả lời cho phù hợp. Nhưng tôi thực sự đánh giá cao bất kỳ tham chiếu nào đến cụm từ "truy vấn tên miền" của Greg Young hoặc Eric Evans, cảm ơn bạn trước. –

2

Tại sao bạn không thể truy vấn các uẩn tham gia?

tôi lấy sự tự do để viết lại mục tiêu:

Gán đội vé để người dùng với tổng tải trọng thấp nhất.

Ở đây chúng ta có một Ticket mà nên có thể tính toán một yếu tố tải trọng tiêu chuẩn, một Team mà biết người sử dụng, và một User mà biết tổng tải của nó và có thể chấp nhận vé mới:

Cập nhật: Nếu nó không cảm thấy đúng để vượt qua một kho lưu trữ để tổng hợp, nó có thể được gói trong một dịch vụ, trong trường hợp này là một định vị. Làm theo cách này làm cho việc thực thi dễ dàng hơn mà chỉ có một tổng hợp được cập nhật tại một thời điểm.

public void AssignTicketToUser(int teamId, int ticketId) 
{ 
    var ticket = repository.Get<Ticket>(ticketId); 
    var team = repository.Get<Team>(teamId); 
    var users = new UserLocator(repository); 
    var tickets = new TicketLocator(repository); 
    var user = team.GetUserWithLowestLoad(users, tickets); 

    user.AssignTicket(ticket); 

    repository.Save(user); 
} 

Ý tưởng là User là tổng hợp duy nhất chúng tôi cập nhật.

Các Team sẽ biết người dùng của nó:

public User GetGetUserWithLowestLoad(ILocateUsers users, ILocateTickets tickets) 
{ 
    User lowest = null; 

    foreach(var id in userIds) 
    { 
     var user = users.GetById(id); 
     if(user.IsLoadedLowerThan(lowest, tickets)) 
     { 
      lowest = user; 
     } 
    } 
    return lowest; 
} 

Cập nhật: Là một vé có thể thay đổi phụ tải theo thời gian, User nhu cầu để tính toán tải trọng hiện tại của nó.

public bool IsLoadedLowerThan(User other, ILocateTickets tickets) 
{ 
    var load = CalculateLoad(tickets); 
    var otherLoad = other.CalculateLoad(tickets); 

    return load < otherLoad; 
} 

public int CalculateLoad(ILocateTickets tickets) 
{ 
    return assignedTicketIds 
     .Select(id => tickets.GetById(id)) 
     .Sum(ticket.CalculateLoad()); 
} 

Các User sau đó chấp nhận vé:

public void AssignTicket(Ticket ticket) 
{ 
    if(ticketIds.Contains(ticket.Id)) return; 

    Publish(new TicketAssignedToUser 
     { 
      UserId = id, 
      Ticket = new TicketLoad 
       { 
        Id = ticket.Id, 
        Load = ticket.CalculateLoad() 
       } 
     }); 
} 

public void When(TicketAssignedToUser e) 
{ 
    ticketIds.Add(e.Ticket.Id); 
    totalLoad += e.Ticket.Load; 
} 

Tôi sẽ sử dụng một trình quản lý quá trình/saga để cập nhật bất kỳ tổng hợp khác.

+0

Vấn đề là tải vé có thể thay đổi dựa trên trạng thái hoặc danh mục của nó chẳng hạn. Có nghĩa là mỗi khi người dùng muốn cập nhật một tixket, tôi sẽ phải cập nhật 2 uẩn mà vi phạm các nguyên tắc/hướng dẫn của DDD – hsen

+0

Nhưng không thể User sau đó tải vé được giao và tính toán tải trọng thực tế? Tôi đoán là tải vé thay đổi tại một thời điểm khác nhau. –

+1

Đó là một giải pháp nhưng đó là một quyết định tôi đã không cam kết chưa: nên tổng hợp có quyền truy cập vào kho? – hsen

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