2009-09-28 19 views
45

Tôi có một kịch bản chung là tôi đang tìm kiếm một số hướng dẫn từ những người có kinh nghiệm về DDD và Mô hình miền nói chung.Cách tránh các mô hình miền thiếu máu hoặc khi di chuyển các phương thức từ các thực thể sang các dịch vụ

Giả sử tôi bắt đầu xây dựng một công cụ blog và yêu cầu đầu tiên là sau khi Bài viết được đăng, người dùng có thể bắt đầu đăng Nhận xét lên đó. Điều này bắt đầu tốt, và dẫn đến việc thiết kế sau:

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

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

My MVC điều khiển được thiết kế như thế này:

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"); 
    } 
} 

Bây giờ tất cả mọi thứ hoạt động tốt, và nó đáp ứng các yêu cầu. Lặp lại tiếp theo, chúng tôi nhận được yêu cầu rằng mỗi khi nhận xét được đăng, tác giả blog sẽ nhận được email thông báo cho anh ấy.

Tại thời điểm này, tôi có 2 lựa chọn mà tôi có thể nghĩ đến. 1) Sửa đổi Điều để yêu cầu một IEmailService (trong ctor?) Hoặc nhận một EmailService từ tham chiếu tĩnh đến vùng chứa DI của tôi

1a) Dường như khá xấu xí. Tôi tin rằng nó phá vỡ một số quy tắc mô hình tên miền mà các thực thể của tôi nhận thức được các dịch vụ?

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) Cũng có vẻ xấu xí, bây giờ tôi yêu cầu vùng chứa DI được định cấu hình được truy cập tĩnh.

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) Tạo IArticleService và di chuyển phương thức AddComment() vào dịch vụ này thay vì trên chính Đối tượng bài viết.

Giải pháp này sạch hơn Tôi tin, nhưng việc thêm nhận xét giờ đây ít bị phát hiện hơn và yêu cầu ArticleService thực hiện công việc. Có vẻ như AddComment nên thuộc về chính class 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"); 
    } 
} 

Vì vậy, về cơ bản tôi tìm kiếm lời khuyên từ những người có nhiều kinh nghiệm trong mô hình miền. Nếu tôi thiếu một giải pháp rõ ràng hơn, vui lòng cho tôi biết :)

Tôi thường không thích cả hai giải pháp một cách trung thực, vì tùy chọn Dịch vụ ít phát hiện hơn. Tôi không còn có thể thêm một bình luận vào một thể hiện của một bài báo mà không có một ArticleService có sẵn. Nó cũng cảm thấy ít tự nhiên hơn, vì AddComment có vẻ như là một phương pháp rõ ràng như vậy trên loại bài viết.

Dù sao thì tôi cũng mong được đọc đầu vào. Cảm ơn trước.

+1

1 cho giải pháp # 2 – JuanZe

+1

1 cho một câu hỏi rõ ràng – Rippo

Trả lời

24

Tôi nghĩ vấn đề cụ thể này có thể được giải quyết một cách thanh lịch với Domain Event.

+0

Sau khi đọc Udi của ba bài viết tôi nghĩ rằng điều này sẽ giải quyết vấn đề của tôi một cách độc đáo. Tuy nhiên, tôi vẫn còn một chút nhầm lẫn về nơi mà các sự kiện miền phải được đăng ký. Bộ điều khiển của tôi có nên đăng ký chúng trong ctor không? Các sự kiện miền có được đăng ký tại Application_Start không? –

+1

Vâng, nếu bạn đào sâu tất cả các nhận xét cho bài đăng đó, bạn sẽ tìm thấy một cuộc thảo luận nhỏ giữa tôi và Udi về việc liệu dịch vụ sự kiện có nên là tĩnh hay không. Cá nhân, tôi sẽ làm cho nó một dịch vụ thể hiện và tiêm nó vào Controller như một phụ thuộc. Tôi đoán Udi sẽ đăng ký nó trong Global.asax ... –

+3

Đây là một kịch bản cơ bản, và sự kiện miền là một cái gì đó thậm chí không có trong sách Evans. Vậy làm thế nào mà mọi người đối phó với nó? – aaimnr

2

Bạn đã cân nhắc việc có bộ điều khiển bài viết về cơ bản truyền một thông báo lên/đăng sự kiện? Sau đó, bất kỳ "người gửi bài viết-sự kiện-đăng-sự kiện" nào sẽ tiêu thụ thông điệp đó và trả lời cho phù hợp; trong trường hợp cụ thể của bạn, một trình thông báo email sẽ lắng nghe những sự kiện đó và được định cấu hình để thực hiện điều đó. Bằng cách này, bài viết bit không cần phải biết bất cứ điều gì về các email thông báo bit.

1

Nhìn qua câu hỏi tuyệt vời này, đã dẫn tôi đọc Employing the Domain Model Pattern từ Udi trên MSDN.

HTH giúp người dùng khác.

Tôi đã cố gắng tìm ra cách để hỏi cùng một câu hỏi này nhưng lại cố gắng lúng túng vài lần. Câu hỏi của bạn chắc chắn là không! Cảm ơn

0

Nếu không sử dụng các sự kiện miền, bạn có thể sử dụng mẫu Văn bản kép và đặt thêm logic cảnh báo bên trong Dịch vụ miền.

Đây là cách nó sẽ như thế nào:

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"); 
    } 
} 

Nếu bạn thích, bạn có thể giữ Comment Logic Thêm vào AddComment Điều, và thay vào đó làm cho các dịch vụ tên miền vào một cái gì đó giống như ICommentAddedProcessor với một CommentAdded () và có Thêm bình luận về Điều nhận xét và ICommentAddedProcessor.

0

Tôi nghĩ bất cứ khi nào chuyên gia miền sử dụng từ "khi" người ta phải xem xét Sự kiện miền hoặc xe buýt sự kiện, đây là ví dụ điển hình theo nghĩa đó.

Tôi đã viết một chi tiết answer trong đó mô tả khi sử dụng xe buýt trường hợp, nó có thể là một đọc tốt xung quanh chủ đề này

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