2009-09-28 7 views
45

Tengo un escenario común en el que estoy buscando orientación de personas con más experiencia con DDD y Modelado de dominios en general.Cómo evitar los modelos de dominio anémicos, o cuándo mover los métodos de las entidades a los servicios

Digamos que comienzo a construir un motor de blog, y el primer requisito es que después de que se publique un artículo, los usuarios puedan comenzar a publicar comentarios en él. Esto comienza bien, y conduce a la siguiente diseño:

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

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

Mi controlador MVC está diseñado de esta manera:

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

Ahora todo funciona bien, y que cumple con el requisito. La siguiente iteración obtenemos el requisito de que cada vez que se publique un comentario, el autor del blog debería recibir un correo electrónico notificándolo.

En este punto, tengo 2 opciones que se me ocurren. 1) Modificar artículo para requerir un servicio de correo electrónico (¿en el controlador?) U obtener un servicio de correo electrónico desde una referencia estática a mi contenedor DI

1a) Parece bastante feo. Creo que rompe algunas reglas del modelo de dominio que mis entidades conocen de los servicios.

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) También parece feo, ahora necesito un contenedor DI configurado al que se accede estáticamente.

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) Crear un IArticleService y mover el método AddComment() para este servicio en lugar de en el propio artículo Entidad.

Esta solución es más limpia, creo, pero Agregar un comentario ahora es menos reconocible y requiere un ArticleService para realizar el trabajo. Parece que AddComment debería pertenecer a la clase de artículo en sí.

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

Así que básicamente busco consejos de personas con más experiencia en el modelado de dominios. Si me falta una solución más obvia, por favor hágamelo saber :)

Por lo general, no me gustan las dos soluciones para ser sincero, porque la opción de Servicio es menos reconocible. Ya no puedo agregar un Comentario a una instancia de un Artículo sin tener un ArtículoServicio disponible. También se siente menos natural, ya que AddComment parece ser un método tan obvio en el tipo de artículo.

De todos modos, espero leer la entrada. Gracias por adelantado.

+1

1 para la solución # 2 – JuanZe

+1

1 para una pregunta clara – Rippo

Respuesta

24

Creo que este problema en particular se puede resolver elegantemente con un Domain Event.

+0

Después de tres artículos de lectura de Udi I Creo que esto resolverá mi problema muy bien. Sin embargo, todavía estoy un poco confundido sobre dónde deben registrarse los eventos del dominio. ¿Mi controlador debería registrarlos en el ctor? ¿Deben registrarse los eventos de dominio en Application_Start? –

+1

Bueno, si repasa todos los comentarios a esa publicación, encontrará una mini-discusión entre Udi y yo sobre si el servicio del evento debe ser estático o no. Personalmente, lo convertiría en un servicio de instancia e inyectarlo en el controlador como una dependencia. Supongo que Udi lo registraría en Global.asax ... –

+3

Este es un escenario tan básico, y Domain Event es algo que ni siquiera estaba presente en el libro de Evans. Entonces, ¿cómo las personas lidiaron con eso? – aaimnr

2

¿Ha considerado que el controlador del artículo esencialmente pase un mensaje hacia arriba/publique un evento? Entonces cualquier "oyente de eventos publicado por artículos" consumiría ese mensaje y respondería en consecuencia; en su caso específico, un notificador de correo electrónico estaría escuchando esos eventos y se configuraría para hacerlo. De esta forma, los bits de publicación de artículos no necesitan saber nada sobre los bits de notificación de correo electrónico.

1

Mirando a través de esta excelente pregunta, me ha llevado a leer Employing the Domain Model Pattern de Udi en MSDN.

HTH ayuda a otros usuarios.

He estado tratando de encontrar la manera de hacer esta misma pregunta pero logré confundirme varias veces. ¡Tu pregunta ciertamente no lo es! Gracias

0

Sin usar eventos de dominio, puede usar el patrón de Despacho doble y poner la lógica AddComment dentro de un Servicio de dominio.

Esta es la forma en que se vería así:

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

Si lo prefiere, puede mantener la lógica comentario Adición en el AddComment del artículo, y en lugar de hacer que el Servicio de dominio en algo así como ICommentAddedProcessor con un CommentAdded () método y tiene AddComment en Article take a Comment y un ICommentAddedProcessor.

0

Creo que cada vez que el experto en el dominio utiliza la palabra "cuando" uno debe considerar eventos de dominio o un bus de eventos, este fue un ejemplo clásico en ese sentido.

He escrito una detallada answer que describe cuándo utilizar los autobuses de eventos, que podría ser una buena lectura en torno a este tema

Cuestiones relacionadas