2015-01-05 101 views
2

我有一個關於在的CQRS原理命令側代碼重複問題。CQRS代碼重複

https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91 https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92

在我看來,在它自己的類分隔每個命令的這種做法將會從數據檢索實體時給予一定的重複代碼:

從下面的文章商店。

也許有點做作,但讓我們說,例如,我有一個命令,我想重置一個用戶密碼給他的電子郵件地址和一個命令,我想更新用戶的最後一個登錄日期 。

public class ResetPasswordCommandHandler : CommandHandler<ResetPasswordCommand> 
{ 
    public override void Execute(ResetPasswordCommand command) 
    { 
     **// duplication here** 
     var user = from c in db.Users 
      where c.EmailAddress = command.EmailAddress 
      select c; 

     user.EmailAddress = command.EmailAddress; 
     ... 
     db.Save(); 
    } 
} 

public class UpdateLastLoginCommandHandler : CommandHandler<UpdateLastLoginCommand> 
{ 
    public override void Execute(UpdateLastLoginCommand command) 
    { 
     **// duplication here** 
     var user = from c in db.Users 
      where c.EmailAddress = command.EmailAddress 
      select c; 

     user.LastLogin = DateTime.Now; 
     ... 
     db.Save(); 
    } 
} 

在這兩個命令中,我根據他的電子郵件地址檢索用戶。現在,如果我想在查詢數據庫之前修剪UI輸入,我將不得不在兩處改變它。

我當然可以創建一個UserRepository,它將例如具有GetUserByEmailAddress方法並將該IUserRepository插入到我的CommandHandlers的構造函數中。然而,這不會最終創建一個「神倉庫」與保存,GetById,GetByUsername等?

如果我創建了一個信息庫,爲什麼創建單獨的查詢對象?

一個如何去維持這段代碼DRY?

回答

3

爲什麼不重構你的命令處理程序到一個實現多個接口:

public class UserCommandHandler : CommandHandlerBase, 
            IHandle<ResetPasswordCommand>, 
            IHandle<UpdateLastLoginCommand> 
{ 
    public void Execute(ResetPasswordCommand command) 
    { 
     var user = GetUserByEmail(command.EmailAddress); 

     user.EmailAddress = command.EmailAddress; 
     ... 
     db.Save(); 
    } 

    public void Execute(UpdateLastLoginCommand command) 
    { 
     var user = GetUserByEmail(command.EmailAddress); 

     user.LastLogin = DateTime.Now; 
     ... 
     db.Save(); 
    } 

    private User GetUserByEmail(string email) { 
      return (from c in db.Users 
        where c.EmailAddress = command.EmailAddress 
        select c).FirstOrDefault(); 
    } 
} 

這樣,您就可以重構命令處理程序內的私人輔助方法,你命令處理程序可以處理類似的命令,和您降低代碼重複。你也不需要「神」存儲庫。

就個人而言,我寧願有私人助手GetUserByEmail作爲一個單獨的查詢類,我通過構造函數注入db背景下,這樣GetUserByEmail是獲取我User一個非常具體的類。

希望這會有所幫助。

+0

不會重構命令處理程序來實現多個接口只是創建一個「神」命令處理程序,而不是一個臃腫的存儲庫? – user4419743

+0

我喜歡第二部分。也許創建一個擴展方法。例如: 'public static IQueryable WithEmailAddress(this IQueryable source,string emailaddress)' – user4419743

1

它不會創造一個神庫。它將是一個具有命令用例所使用方法的正確存儲庫。但是這意味着你使用了正確的Repository模式,這意味着沒有IQueryable或EF暴露。請記住,域存儲庫具有僅由域用例需要的「查詢」方法,而存儲庫僅處理域聚合根(整個域對象)。

你目前的方法是在你的應用服務使用EF(到底DAO),這意味着,以及連接到EF應用層和可能的領域層。你的高級服務(一個命令處理程序最終是一個服務,實現一個用例)不應該執行查詢,因爲從他們的角度來看,沒有rdbms,即他們不知道查詢。

如果您的應用程序是很簡單的,或者你知道它不會在未來發生改變了,那麼你可以直接使用EF的快捷方式,但如果你決定把事情簡單化這樣,那麼你不需要CQRS和基於消息的體系結構。

1

我知道它看起來像代碼重複,我知道它看起來像你違背DRY校長,但我可以向你保證在行爲中使用共享服務存儲庫代碼從來就不是一個好主意。其中一個問題是,每個行爲實際上可能需要通過電子郵件調用實現GetUser的稍微不同。一個我要求全名,另一個可能不是。通過共享此代碼,您可以有效地緊密耦合兩個呼叫。現在,一種行爲可能需要返回一塊額外的數據,但現在,由於您在其他實現調用此「共享服務」時緊緊地聯繫了您,因此它具有所需的額外數據的開銷。

如果要在執行存儲庫調用時共享代碼,請使用稱爲規範模式和/或策略模式的內容。你不會在上面的例子中遇到任何令人討厭的緊耦合問題,並且作爲獎勵,你的代碼會更好地讀取,因爲意圖在前面而不是實現。