Уведомления по электронной почте - в домене объекта или службы? - PullRequest
3 голосов
/ 30 мая 2009

Я ищу рекомендации о том, как подойти к следующей проблеме проектирования (используя вымышленный пример, основанный на stackoverflow). Я бы старался избегать анемичной модели предметной области и обращался за советом к «наилучшей практике» для этого типа случаев.

Сценарий:

Предположим, разрабатывается новая функция для stackoverflow, которая отправляет уведомление по электронной почте владельцу вопроса всякий раз, когда его / ее вопрос получает 10 голосов.

Объектная модель домена выглядит примерно так:

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

    public void AddUpvote(Vote upvote)
    {
        Upvotes.Add(upvote);
    }
}

Потенциальные реализации:

  1. Измените AddUpvote() на параметр IEmailerService и выполните логику в методе AddUpvote().

    public void AddUpvote(Vote upvote, IEmailerService emailer)
    {
        Upvotes.Add(upvote);
        if ( Upvotes.Count == 10 )
        {
            emailer.Send(Owner.EmailAddr);
        }
    }
    
  2. Обнаружение этого состояния в AddUpvote() и AddUpvote() разрешают IEmailService из контейнера IoC (вместо передачи IEmailerService в качестве параметра).

  3. Обнаружить это состояние во внешнем сервисном объекте, который вызывает question.AddUpvote().

    public void UpvoteClickHandler(Question question)
    {
        question.AddUpvote(new Upvote());
        if ( question.Upvotes.Count == 10 )
        {
            _emailer.Send(question.Owner.EmailAddr);
        }
    }
    
  4. Ваше лучшее решение здесь!

Ответы [ 4 ]

5 голосов
/ 30 мая 2009

Вы действительно не хотите смешивать эти два, потому что у них есть отдельные проблемы. Пусть класс Вопрос позаботится о вопросах, а служба сообщений позаботится о том, что делать, когда голосование достигнет 10, или 20, или 100, или ...

Следующий пример предназначен только для демонстрационных целей, но вы получите точку. Существует четкое разделение интересов, поэтому класс вопросов не должен изменяться, если изменяются требования к отправке сообщений. Помните, что согласно принципам 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;
    }
}

Итак, у вас это есть. Есть много других способов сделать это, но модель событий - отличный способ, и она позволяет разделить задачи, которые вы хотите реализовать в своей реализации, чтобы выполнить обслуживание раньше.

2 голосов
/ 30 мая 2009

Оба варианта 1) и 2) выглядят как неподходящее место для отправки электронного письма. Экземпляр Вопроса не должен знать этих двух вещей:

  1. Он не должен знать о политике, то есть когда отправлять электронную почту.
  2. Он не должен знать о механизме уведомления для политики, то есть службы электронной почты.

Я знаю, что это дело вкуса, но вы тесно связываете Вопрос с политикой, а также с механизмом отправки электронной почты. Было бы очень трудно переместить этот класс Question в другой проект (например, ServerFault, который, например, является дочерним сайтом StackOverflow)

Меня интересует этот вопрос, потому что я создаю систему уведомлений для создаваемой справочной службы. Вот что я сделал в своей системе:

Создание NotificationManager (по сути, полностью перенесите проблему уведомлений в отдельный класс).

public Class NotificationManager
{
    public void NotificationManager(NotificationPolicy policy, IEmailService emailer)
    {
    }
}

Затем я сделал что-то вроде этого (UpvoteClickHandler зависит от экземпляра NotificationManager):

public void UpvoteClickHandler(Question question)
{
    question.AddUpvote(new Upvote());
    _notificationManager.Notify(Trigger.UpvoteAdded, question);
}

Все, что делает UpvoteClickHandler, - это сообщает NotificationManager, что в вопрос был добавлен ответ, и позволяет NotificationManager определить, следует ли отправлять электронное письмо и каким образом.

1 голос
/ 28 июля 2009

Привет всем,

По моему мнению, «отправка уведомления по электронной почте владельцу вопроса всякий раз, когда его / ее вопрос получает 10 голосов» - это логика домена, и поэтому он должен быть в объекте домена, чтобы избежать анемичного домена.

Это действие по отправке электронной почты (т. Е. Связи с SMTP-сервером), которая ДОЛЖНА перейти на уровень инфраструктуры.

Так что я думаю, что вариант 1 не совсем неправильный. Имейте в виду, что вы всегда можете протестировать свой объект, передав ложную реализацию IEmailerService.

С наилучшими пожеланиями,

Stefano

1 голос
/ 30 мая 2009

Ответ зависит от вашего фундаментального подхода к разработке приложений и объектов. И (отредактируйте здесь) то, что вы считаете своей самой важной чертой системы. Похоже, у вас есть данные, вопросы и бизнес-правила, до голосов. Не вопрос объектов вообще. Таким образом, вы должны обрабатывать свои данные как данные и позволять инструментам данных работать с ними, не смешивая поведение с ними. Традиционный объектный дизайн будет иметь все поведения и данные в объекте, поэтому отправка электронной почты будет принадлежать объекту. (вариант 1 и 2) Я думаю, это черный ящик или автономный объектный подход. Современные практики, как я узнал, имеют объекты в качестве простых носителей данных. Которые должны быть перемещены, сохранены, преобразованы и имеют дело с ними. Возможно, жить как нечто большее, чем структуры в C. Поведение происходит от услуг и преобразований, которые применяются к простым объектам.

...