电子邮件通知 - 在域对象或服务中?

Kev*_*lin 3 domain-driven-design business-logic

我正在寻找有关如何处理以下设计问题的建议(使用基于stackoverflow的虚构示例).我试图避免贫血领域模型,并寻求针对此类案例的一般"最佳实践"建议.

场景:

假设正在为stackoverflow开发一个新功能,只要他/她的问题收到10个upvotes就会向问题所有者发送电子邮件通知.

域对象模型是这样的:

public class Question
{
    string Question { get; set; }
    IList<Votes> Upvotes { get; set; }
    User Owner { get; set; }

    public void AddUpvote(Vote upvote)
    {
        Upvotes.Add(upvote);
    }
}
Run Code Online (Sandbox Code Playgroud)

潜在的实施:

  1. 更改AddUpvote()以获取IEmailerService参数并在AddUpvote()方法中执行逻辑.

    public void AddUpvote(Vote upvote, IEmailerService emailer)
    {
        Upvotes.Add(upvote);
        if ( Upvotes.Count == 10 )
        {
            emailer.Send(Owner.EmailAddr);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 检测此状态AddUpvote()AddUpvote()从IoC容器中解析IEmailService(而不是将IEmailerService作为参数传递).

  3. 在调用的外部服务对象中检测此状态question.AddUpvote().

    public void UpvoteClickHandler(Question question)
    {
        question.AddUpvote(new Upvote());
        if ( question.Upvotes.Count == 10 )
        {
            _emailer.Send(question.Owner.EmailAddr);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 你的解决方案更好!

Jos*_*osh 5

你真的不想将这两者混合起来,因为他们有不同的顾虑.让问题类关注问题,并且消息服务关心当投票达到10,或20或100或...时要做什么

以下示例仅用于演示目的,但您将明白这一点.关注点明显分开,因此如果发送消息的要求发生变化,则不必更改Question类.请记住,根据SOLID原则,一个类应该只有一个改变的理由.

public class Question
{
    public string Description { get; set; }
    public Int32 Votes { get; set; }
    public User Owner { get; set; }

    public event EventHandler<QuestionEventArgs> OnUpvote;

    private void RaiseUpvoteEvent(QuestionEventArgs e)
    {
        var handler = OnUpvote;
        if (handler != null) handler(this, e);
    }

    public void Upvote()
    {
        Votes += 1;

        RaiseUpvoteEvent(new QuestionEventArgs(this));
    }
}

public class MessageService
{
    private Question _question;

    public MessageService(Question q)
    {
        _question = q;

        q.OnUpvote += (OnUpvote);
    }

    private void OnUpvote(object sender, QuestionEventArgs e)
    {
        if(e.Question.Votes > 10)
            SendMessage(e.Question.Owner);
    }
}

public class QuestionEventArgs: EventArgs
{
    public Question Question { get; set; }

    public QuestionEventArgs(Question q)
    {
        Question = q;
    }
}
Run Code Online (Sandbox Code Playgroud)

所以你有它.还有很多其他方法可以实现这一目标,但事件模型是一个很好的方法,它可以在您的实现中实现您想要的关注点分离,从而提前进行维护.