2009-09-28 63 views
45

我有一個常見的場景,我正在尋找一些有關DDD和領域建模更有經驗的人的一些指導。如何避免貧血域模型或何時將方法從實體移動到服務中

假設我開始構建博客引擎,第一個要求是在發佈文章後,用戶可以開始發佈評論。這將啓動罰款,並導致了以下設計:

public class Article 
{ 
    public int Id { get; set; } 

    public void AddComment(Comment comment) 
    { 
     // Add Comment 
    } 
} 

我的MVC控制器的設計是這樣的:

public class ArticleController 
{ 
    private readonly IRepository _repository; 

    public ArticleController(IRepository repository) 
    { 
     _repository = repository; 
    } 

    public void AddComment(int articleId, Comment comment) 
    { 
     var article = _repository.Get<Article>(articleId); 
     article.AddComment(comment); 
     _repository.Save(article); 
     return RedirectToAction("Index"); 
    } 
} 

現在一切正常,並符合要求。接下來的迭代中,我們得到一個要求,每次發佈評論時,博客作者都應該收到一封電子郵件通知他。

在這一點上,我有兩個選擇,我可以想到。 1)修改文章要求IEmailService(在ctor?中)或從靜態引用獲取EmailService到我的DI容器

1a)看起來很醜陋。我相信它打破了我的實體知道服務的一些域模型規則?

public class Article 
{ 
    private readonly IEmailService _emailService; 

    public Article(IEmailService emailService) 
    { 
     _emailService = emailService; 
    } 

    public void AddComment(Comment comment) 
    { 
     // Add Comment 

     // Email admin 
     _emailService.SendEmail(App.Config.AdminEmail, "New comment posted!"); 
    } 
} 

1b)也似乎醜陋,我現在需要一個靜態訪問配置DI容器。

public class Article 
{ 
    public void AddComment(Comment comment) 
    { 
     // Add Comment 

     // Email admin 
     var emailService = App.DIContainer.Resolve<IEmailService>(); 
     emailService.SendEmail(App.Config.AdminEmail, "New comment posted!"); 
    } 
} 

2)創建一個IArticleService並將AddComment()方法移動到此服務而不是文章實體本身。

我相信這個解決方案更乾淨,但添加評論現在不易發現,需要ArticleService來完成這項工作。似乎AddComment應該屬於Article類本身。

public class ArticleService 
{ 
    private readonly IEmailService _emailService; 

    public ArticleService(IEmailService emailService) 
    { 
     _emailService = emailService; 
    } 

    public void AddComment(Article article, Comment comment) 
    { 
     // Add comment 

     // Email admin 
     _emailService.SendEmail(App.Config.AdminEmail, "New comment posted!"); 
    } 

} 


public class ArticleController 
{ 
    private readonly IRepository _repository; 
    private readonly IArticleService _articleService; 

    public ArticleController(IRepository repository, IArticleService articleService) 
    { 
     _repository = repository; 
     _articleService = articleService; 
    } 

    public void AddComment(int articleId, Comment comment) 
    { 
     var article = _repository.Get<Article>(articleId); 
     _articleService.AddComment(article, comment); 
     _repository.Save(article); 
     return RedirectToAction("Index"); 
    } 
} 

所以,我基本上尋找更多領域建模經驗的人的意見。如果我錯過了一個更明顯的解決方案,請讓我知道:)

我通常不喜歡這兩種解決方案是誠實的,因爲服務選項不易發現。如果沒有可用的ArticleService,我不能再向文章的實例添加評論。它也感覺不那麼自然,因爲AddComment看起來就像是一種明顯的Article類型的方法。

無論如何,我期待閱讀輸入。提前致謝。

+1

+1的解決方案#2 – JuanZe 2009-09-28 19:02:57

+1

+1很好看的一個清晰的問題 – Rippo 2010-02-01 08:01:23

回答

24

我認爲這個問題可以用Domain Event來優雅地解決。

+0

讀烏迪的三篇文章後,我認爲這將很好地解決我的問題。不過,對於域名事件應該註冊在哪裏,我仍然有點困惑。我的控制器是否應該將它們註冊到ctor中?應該在Application_Start註冊域事件嗎? – 2009-09-28 19:57:21

+1

好吧,如果你仔細查看那篇文章的所有評論,你會發現我和Udi之間關於事件服務是否應該是靜態的迷你討論。就我個人而言,我將它作爲一個實例服務,並將其作爲依賴注入到Controller中。我想Udi會在Global.asax中註冊它... – 2009-09-28 20:47:54

+3

這是一個這樣的基本場景,而域名事件甚至還沒有出現在埃文斯的書中。那麼人們如何應對呢? – aaimnr 2009-12-05 13:50:50

2

您是否認爲文章控制器本質上是通過郵件發送/發佈事件?然後,任何「文章發佈事件監聽器」都會使用該消息並作出相應的響應;在您的具體情況下,電子郵件通知程序會監聽這些事件並進行配置。通過這種方式,文章發佈位不需要知道關於電子郵件通知位的任何信息。

1

回顧這個優秀的問題,讓我在MSDN上從Udi讀取Employing the Domain Model Pattern

HTH幫助其他用戶。

我一直在努力解決如何問這個同樣的問題,但設法多次困惑自己。你的問題當然不是!謝謝

0

如果不使用域事件,可以使用Double Dispatch模式並將AddComment邏輯放入域服務中。

這是它會是什麼樣子:

public class Article 
{ 
    public void AddComment(Comment comment, IAddCommentProcessor commentProcessor) 
    { 
     commentProcessor.AddComment(this, comment); 
    } 
} 

public interface IAddCommentProcessor 
{ 
    void AddComment(Article article, Comment comment); 
} 

public class AddCommentAndEmailProcessor : IAddCommentProcessor 
{ 
    private readonly _emailService; 
    public AddCommentAndEmailProcessor(EmailService emailService) 
    { 
     _emailService = emailService; 
    } 

    public void AddComment(Article article, Comment comment) 
    { 
     // Add Comment 

     // Email 
     _emailService.SendEmail(App.Config.AdminEmail, "New comment posted!"); 
    } 
} 

public class ArticleController 
{ 
    private readonly IRepository _repository; 
    private readonly IArticleService _articleService; 

    public ArticleController(IRepository repository, IArticleService articleService) 
    { 
     _repository = repository; 
     _articleService = articleService; 
    } 

    public void AddComment(int articleId, Comment comment) 
    { 
     var article = _repository.Get<Article>(articleId); 
     article.AddComment(comment, new AddCommentAndEmailProcessor(ServiceLocator.GetEmailService())); // Or you can use DI to get the Email Service, or any other means you'd prefer 
     _repository.Save(article); 
     return RedirectToAction("Index"); 
    } 
} 

如果你願意,你可以保持在文章的AddComment的添加評論的邏輯,而是使域名服務變得像ICommentAddedProcessor用CommentAdded( )方法,並對Article進行AddComment評論和ICommentAddedProcessor。

0

我認爲,只要領域專家使用這個詞,當「」必須考慮域事件或事件總線時,這是一個典型的例子。

我已經寫描述時使用事件總線的詳細answer,這可能是圍繞這個話題