2009-09-28 7 views
45

Ho uno scenario comune che sto cercando alcune indicazioni da persone più esperte con DDD e Domain Modeling in generale.Come evitare i modelli di dominio anemici o quando spostare i metodi dalle entità nei servizi

Dire che inizio a creare un motore per blog e il primo requisito è che dopo aver pubblicato un articolo, gli utenti possano iniziare a postare commenti su di esso. Questo inizia bene, e porta alla seguente disegno:

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

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

mio MVC regolatore è progettato in questo modo:

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

Ora tutto funziona bene, e soddisfa il requisito. Prossima iterazione otteniamo il requisito che ogni volta che viene pubblicato un commento, l'autore del blog debba ricevere un'e-mail che lo avvisa.

A questo punto, ho 2 scelte a cui posso pensare. 1) Modificare l'articolo per richiedere un IEmailService (nel ctor?) O ottenere un EmailService da un riferimento statico al mio contenitore DI

1a) Sembra abbastanza brutto. Credo che rompa alcune regole del modello di dominio che le mie entità sono a conoscenza dei servizi?

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) Inoltre sembra brutto, ora richiede un contenitore DI configurato a cui si accede staticamente.

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) Creare un IArticleService e spostare il metodo addComment() a questo servizio, invece di sull'articolo entità stessa.

Questa soluzione è più pulita, ma l'aggiunta di un commento è ora meno rilevabile e richiede un ArticleService per eseguire il lavoro. Sembra che AddComment debba appartenere alla classe Article stessa.

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

Quindi sono fondamentalmente alla ricerca di consigli da persone più esperte nella modellazione di domini. Se mi manca una soluzione più ovvia per favore fatemelo sapere :)

In genere non mi piacciono entrambe le soluzioni, perché l'opzione Servizio è meno rilevabile. Non è più possibile aggiungere un commento a un'istanza di un articolo senza disporre di un articoloServizio disponibile. Sembra anche meno naturale, dal momento che AddComment sembra un metodo così ovvio per il tipo di articolo.

In ogni caso non vedo l'ora di leggere l'input. Grazie in anticipo.

+1

+1 per soluzione # 2 – JuanZe

+1

+1 per una chiara domanda – Rippo

risposta

24

Penso che questo particolare problema possa essere risolto elegantemente con un Domain Event.

+0

Dopo tre articoli di lettura di Udi I penso che questo risolverà bene il mio problema. Tuttavia, sono ancora un po 'confuso su dove gli eventi del dominio devono essere registrati. Il mio controller dovrebbe registrarli nel ctor? Gli eventi del dominio dovrebbero essere registrati su Application_Start? –

+1

Bene, se analizzi tutti i commenti su quel post, troverai una mini discussione tra me e Udi sul fatto che il servizio eventi debba essere statico o meno. Personalmente, lo renderei un servizio di istanza e inserirlo nel controller come dipendenza. Immagino che Udi lo registrerebbe in Global.asax ... –

+3

Questo è uno scenario così fondamentale, e Domain Event è qualcosa che non era nemmeno presente nel libro di Evans. Quindi, come hanno fatto le persone a farcela? – aaimnr

2

Hai mai pensato che il controller dell'articolo passasse essenzialmente un messaggio su/pubblica un evento? Quindi ogni "articolo-post-evento-ascoltatori" consumerebbe quel messaggio e risponderà di conseguenza; nel tuo caso specifico, un notificatore di e-mail sarebbe in ascolto per quegli eventi e sarà configurato per farlo. In questo modo l'articolo che pubblica i bit non ha bisogno di sapere nulla dei bit di notifica email.

1

Guardando attraverso questa eccellente domanda, mi ha portato a leggere Employing the Domain Model Pattern da Udi su MSDN.

HTH aiuta altri utenti.

Ho cercato di capire come fare questa stessa domanda ma sono riuscito a confondermi diverse volte. La tua domanda certamente non lo è! Grazie

0

Senza utilizzare gli eventi di dominio, è possibile utilizzare il modello di doppia spedizione e inserire la logica AddComment all'interno di un servizio di dominio.

Ecco come sarebbe simile:

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

Se si preferisce, è possibile mantenere la logica Aggiunta Commento alla addComment dell'articolo, e invece rendere il servizio Domain in qualcosa di simile ICommentAddedProcessor con un CommentAdded () metodo e avere AddComment su articolo prendere un commento e un ICommentAddedProcessor.

0

Penso che ogni volta che l'esperto del dominio utilizza la parola "quando" si deve considerare Eventi di dominio o un bus di eventi, questo è stato un classico esempio in tal senso.

Ho scritto una dettagliata answer che descrive quando utilizzare gli autobus di eventi, potrebbe essere una buona lettura intorno a questo argomento

Problemi correlati