Свойства объекта предметной области и инкапсуляция

У меня есть объект электронной почты с двумя свойствами: меткой и значением. Пользователь системы должен подтвердить свою электронную почту, прежде чем он сможет использовать ее в системе. Процесс проверки очень прост:

  1. Установите код активации для электронной почты
  2. Отправьте электронное письмо с кодом активации, чтобы убедиться, что электронное письмо действительно.

Объект электронной почты выглядит следующим образом:

class Email {
    String label
    String value
    EmailStatus status
    String activationCode

    boolean requestVerification() {
        // Set the activationCode that will be refereced to change the email status 
        this.activationCode = UUID
        // Raise an event to send a notification email by a communication service
        EventManager.fire('emailVerificationRequest',this)
    }

Все работает нормально, за исключением того, что свойство activationCode не подходит для объекта Email. Он никоим образом не описывает состояние объекта и используется только в процессе проверки электронной почты. Поэтому я изменил свой код, включив в него новый объект под названием ActivationToken. Объект будет использоваться для инкапсуляции кода активации. Вот новая версия объекта электронной почты

 class Email {
    String label
    String value
    EmailStatus status

    boolean requestVerification() {
        new ActivationToken(target:this,code:UUID,expiryDate:new Date()).save()
        // Raise an event to send a notification email by a communication service
        EventManager.fire('emailVerificationRequest',this)
    }

 class ActivationToken {
    Email target
    String code
    Date expiryDate
 }
  1. Это хороший дизайн домена или я зря усложняю свой объект?
  2. Принадлежит ли метод requestVerification объекту Email в первую очередь или его следует разместить в другом месте; в службе или в процессе.
  3. Есть ли какой-либо шаблон проектирования, которому я могу следовать для решения подобных проблем?

Обновить

Я хотел объяснить, почему я сохранил метод requestVerfication частью объекта домена электронной почты даже после второго подхода к рефакторингу.

У меня есть удаленный интерфейс, который напрямую взаимодействует с объектами домена через диспетчер следующим образом:

remote://email/6/do/requestVerification

Первоначально я хотел, чтобы вся связь с серверной частью направлялась через объекты домена, и если есть необходимость взаимодействовать, скажем, со службой, которую я использовал IOC, чтобы внедрить ее в объект домена и использовать объект домена в качестве прокси. Разделение между удаленным интерфейсом и объектом домена выглядело четким, но это оказалось очень плохой идеей, так как оно нарушает дизайн домена и вводит бесполезную зависимость от внешнего объекта (в данном случае EmailVerificationService ), который не имеет ничего общего с поведением или аспекты состояния доменного объекта

Другим решением для решения этой проблемы может быть сохранение метода requestVerification в службе, которой он естественно принадлежит, и введение нового синтаксиса в протокол связи, например:

 remote://service/email/do/requestVerification

Ребята, что вы думаете ?

Спасибо

-кен


person ken    schedule 03.09.2010    source источник


Ответы (3)


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

Что касается ваших индивидуальных вопросов:

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

  2. Как уже упоминалось, я действительно думаю, что это ответственность на уровне службы и должна быть в какой-то службе EmailVerificationService. В самой модели домена вас действительно заботит только действительность электронной почты, а не средства, с помощью которых она проверяется.

  3. На мой взгляд, вы уже используете лучший из возможных подходов. Шина событий с событиями, вызванными объектом домена, является чистой и надежной.

person Joseph Ferris    schedule 03.09.2010
comment
Пожалуйста, ознакомьтесь с обновлением моего вопроса, в котором объясняется причина, по которой я изначально не выбрал сервисный подход. Ваши комментарии очень ценятся. Спасибо - person ken; 03.09.2010

Вы предприняли правильные шаги, отделив Email от ActivationToken — концептуально это разные вещи (кстати, для ясности я бы использовал EmailActivationToken). ).

Как правило, EmailVerificationService используется для инкапсуляции логики проверки.

person Vijay Patel    schedule 03.09.2010
comment
Пожалуйста, ознакомьтесь с обновлением моего вопроса, в котором объясняется причина, по которой я изначально не выбрал сервисный подход. Ваши комментарии очень ценятся. Спасибо - person ken; 03.09.2010

Я считаю хорошей идеей инкапсулировать функциональность в ActivationToken. Но, инициализировав ActivationToken в классе Email, вы создали скрытую зависимость от внешнего ресурса. Это эффективно затрудняет модульное тестирование Email и повторное использование другими клиентами, у которых есть другая схема активации.

Вместо этого вы можете использовать шаблон внедрение зависимостей/инверсия управления для внедрения ActivationToken в Email класс. Это позволяет внедрять различные стратегии активации и открывает возможности для простого модульного тестирования класса Email.

Для этого вам понадобится интерфейс для ActivationToken, а конструктор класса Email должен ссылаться на объект, реализующий этот интерфейс:

interface IActivationToken
{
   void Save(object target, string code, DateTime date);
}

class Email
{
    IActivationToken token;
    // Other properties go here.

    public Email(IActivationToken token)
    {
        this.token = token;
    }

    boolean RequestVerification() 
    {    
        token.Save(this, code, date);        
        // Raise an event to send a notification email by a communication service    
        EventManager.fire('emailVerificationRequest', this)    
    }    
}

Теперь класс Email не имеет скрытых зависимостей от внешних ресурсов.

person Jakob Christensen    schedule 03.09.2010
comment
Пожалуйста, ознакомьтесь с обновлением моего вопроса, в котором объясняется причина, по которой я изначально не выбрал сервисный подход. Ваши комментарии очень ценятся. Спасибо - person ken; 03.09.2010