views:

55

answers:

3

I have an Email object with two properties, label and value. System user need to verify their email before they can use it in the system. The verification process is very simple:

  1. Set an activation code for the email
  2. Send an email with the activation code to verify that the email is valid

The email object looks like the following:

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

All works fine, except that the activationCode property doesn't feel right within the Email object. It doesn't describe in anyway the state of the object and it's only used in the email validation process. So I modified my code to introduce a new object called ActivationToken. The object will be used to encapsulate the activationCode. Here's the new version of the email object

 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. Is this a sound Domain Design or am I complicating my object for nothing
  2. Does the requestVerification method belong to the Email object in the first place or should it be placed elsewhere; in a service, or within a process.
  3. Is there any design pattern that I can follow to tackle similar problems

Update

I wanted to explain why i kept the requestVerfication method part of the Email domain object even after the second refactoring approach.

I have a remote interface that interacts directly with domain objects through a dispatcher in the following fashion:

remote://email/6/do/requestVerification

Originally i wanted to keep all the communication with the backend channeled through the domain objects and if there is a need to interact, say, with a service i was using IOC to inject it into the domain object and use the domain object as a proxy. The separation between the remote interface and the domain object looked clean but that turned out to be a very bad idea as it beaks the domain design and introduces useless dependency to external object (in this case EmailVerificationService ) that has nothing to do with the behavior or the state aspects of the domain object

The other solution to solve this could be to keep the requestVerification method in a service where it naturally belongs and introduce a new syntax to the communication protocol such as:

 remote://service/email/do/requestVerification

What do you guys think ?

Thank you

-ken

A: 

You've taken the right steps in separating Email from ActivationToken - conceptually they're separate things (btw, I would have used EmailActivationToken for clarity).

Typically, an EmailVerificationService would be used to encapsulate the verification logic.

Vijay Patel
Please see the update to my question that explain the reason why i didn't opt for a service approach in the first place. You comments are much appreciated. Thank you
ken
+1  A: 

Personally, I find both approaches to be acceptable, but would prefer the second. What I would use to determine which approach, would be the input of the domain expert. When modelling the domain, was the activation code something explicitly part of the requirement, or do you have a reason to maintain it after the email account have been validated? If you do not have an explicit reason to go with your first approach, I would stick with your second.

In regards to your individual questions:

  1. I wouldn't go as far as to consider it being complicating your object, more of abstracting service-level concerns from the domain. Yes, it is more code, and more work, but is is probably the better approach - unless you have an explicit reason to use the original approach.

  2. As alluded to, I really think that this is a service-level responsibility, and should be in some sort of EmailVerificationService. In the domain model itself, you only really care if the email is valid, not the means by which it is validated.

  3. You are already using the best approach that you can, in my opinion. An eventing bus with events raised from the domain object is clean and reliable.

joseph.ferris
Please see the update to my question that explain the reason why i didn't opt for a service approach in the first place. You comments are much appreciated. Thank you
ken
A: 

I think it is a good idea to encapsulate the functionality in ActivationToken. But by initializing ActivationToken within the Email class you have created a hidden dependency on an external resource. This effectively makes the Email hard to unit test and to reuse by other clients that have another scheme for activation.

Instead you may want to use the Dependency Injection/Inversion of Control pattern to inject the ActivationToken into the Email class. This allows you to inject different activation strategies and opens up for easy unit testing of the Email class.

To do this you'd need an interface for ActivationToken and the constructor of the Email class should take reference to an object implementing this interface:

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

Now the Email class have no hidden dependencies on external resources.

Jakob Christensen
Please see the update to my question that explain the reason why i didn't opt for a service approach in the first place. You comments are much appreciated. Thank you
ken