Ответ 1
Я думаю, что эта конкретная проблема может быть решена элегантно с Событие домена.
У меня есть общий сценарий, что я ищу некоторые рекомендации от людей, более опытных с DDD и Domain Modeling в целом.
Скажем, я начинаю строить механизм блога, и первое требование состоит в том, что после публикации статьи пользователи могут начинать публиковать комментарии к нему. Это начинается нормально и приводит к следующему дизайну:
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");
}
}
Поэтому я в основном ищу совет от людей, более опытных в области моделирования доменов. Если у меня отсутствует более очевидное решение, сообщите мне:)
Мне вообще не нравятся оба решения, если честно, потому что опция Service менее понятна. Я больше не могу добавлять комментарий к экземпляру статьи без наличия доступной статьи. Это также кажется менее естественным, поскольку AddComment кажется таким очевидным методом для типа статьи.
В любом случае я с нетерпением жду чтения ввода. Спасибо заранее.
Я думаю, что эта конкретная проблема может быть решена элегантно с Событие домена.
Считаете ли вы, что контроллер статьи по существу передает сообщение вверх/вывешивает событие? Тогда любые "прослушиватели, опубликованные в журнале", будут потреблять это сообщение и соответственно реагировать; в вашем конкретном случае оповещение по электронной почте будет прослушивать эти события и настраиваться для этого. Таким образом, биты публикации статьи не должны знать ничего о битах уведомления по электронной почте.
Просматривая этот прекрасный вопрос, я заставил читать использование шаблона модели домена из Udi в MSDN.
HTH помогает другим пользователям.
Я пытался разобраться, как задавать этот вопрос, но несколько раз путался. Ваш вопрос, конечно же, нет! Благодаря
Без использования событий домена вы можете использовать шаблон 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() и добавить AddComment для статьи и комментарий ICommentAddedProcessor.
Я думаю, всякий раз, когда эксперт домена использует слово "когда", нужно учитывать события домена или шину событий, это был классический пример в этом смысле.
Я написал подробный ответ , в котором описывается, когда использовать шины событий, возможно, это хорошо читается в этом разделе