How to attack this

Before I go out and just blindly start writing this feature into my application I thought I would pick your brains. In this application the system will send out notifications to the users when certain things happen

  • user registers

  • registration approves

  • place an order

  • order status changes

  • change their password

  • when admin adds data to their order
    admins will receive notifications when

  • user registers

  • places order
    There might be more but that is the start of it. Each notification will be added to the database and an email will be sent to the appropriate email address. I started out thinking I would go down this road. Basically in the controller like account.register we call our service with a “type” but this seems tightly coupled.

public void function register(){

// registration code happens here

// notification
notificationService.addAndSend(“Registration Completed”,user);

}

public void function addAndSend(String notification,User user){
var n = notificationService.new();

switch(notification){
case “Registration Completed” : {
n.setSubject(“Registration Completed”);
n.setBody(“read_in_template_here”);
break;
}
}

// email notification

// add new notification
save(n);

is this a good case for interceptor? announce an interception point and have a couple different interceptors? I just don’t know what the best way to tackle this is. Instead of blindly writing it out and refactoring later I think this is a good time to have a good plan! Thanks for the help peeps!

Hey Dan,

I’ll get stuck into some idea generation with you, notifications from apps always intrigues me and there doesn’t seem to be much in the way of advice on how it should best be done, even though it’s a really common challenge.

First of all - I love the concept of the interceptor - It’s not something I’ve tried myself but as notifications are a cross cutting concern, it might be well placed. I would try a little proof-of-oncept and see how it feels.

Now, in one of my non-coldbox applications I have a similar setup to you, I send notifications on given events, which then also get saved to the database so we have a record of when and what was sent.

I recently built a model that looks something like this:

MailService - The service responsible for the mail dispatch, using cfmail, rest or whatever.
Message - An ORM entity for the message that contains To, From, Subject, HTML, TEXT etc.
Mailer - A base mailer object, used for establishing different messages.

Now, the clever bit in my opinion is the mailer class. The idea is that it allows me to separate out email generation from the rest of the model, so it’s not sat inside services, this just felt cleaner to me.

Within the application I then have a ‘mailers’ directory, which contains a set of CFC’s - each of which extends the base mailer and contains methods that each define a different message.

So, for instance I would have.

/mailers/user_mailer.cfc
welcome(user)
approved(user)
forgotten_password(user)

Each of the methods, creates, populates and returns a message object. ready for sending through the mail service. So the welcome method might look something like this.

welcome (user) {
local.message = newMessage();

local.message.to = user.getEmail();
local.message.subject = ‘welcome’

local.message.context = {
to = user.getName(),
signature = ‘The Team’
};

return message;
}

Now, each of these mailer classes and their methods has a corresponding set of views, which are located by convention based on the mailers class and method name, something like:

/views/email/user/welcome.html
/views/email/user/welcome.txt

These are templates for the html and plain text versions of the email, in my case these are mustache templates, with placeholders within them for any dynamic context {{To}} or {{Signature}} for instance.

The values for these are put together inside the mailer method we saw above, notice the context?

Also notice there are two templates? one for the HTML format and one for the Plain Text format.

Now, when I want to start sending emails I inject my mailers into my service, so within my registration service I can then do something like this:

public void function register(){

// registration code happens here

// notification
user_mailer.send_welcome(user);

}

Notice that I actually used ‘send_welcome()’ instead of just ‘welcome()’ this is because the mailer class is smart enough to use OMM so I can instantly dispatch the message if I want, or I could just call welcome() and get the mail object for manipulation and dispatch later.

I would then have my mail service save that entity as it was pushed out of the door.

Robert

Dan,

I have an interceptor called Dispatcher which I use to accomplish exactly this. Each time an event occurs I announce the appropriate interception point to send the email, send SMS, etc. I’m sure there I argument to put this into services as the more interception point I add the more code duplication i seem to have with setting up the mail object. For what it’s worth it works quite well and allows me to call the interception points from whatever point in my app.

Nolan Dubeau

Load .,8,1

@Robert - that is what I had in mind to begin with but you gave me some great ideas so thank you!

@Nolan - So in my case I would have 2 interceptors then right… 1 that adds to the database and 1 that send the email? Any chance you would be willing to share your interceptor so I can get an idea of what your doing?

Dan,

I suppose you could use two interceptors but I use one. At the end of each mail I call a private function in the interceptor called logNotification that logs the specifics of the notification to the db as well as the rendered body of the email. I can post more later. On mobile at the moment.

N

Nolan Dubeau

Load .,8,1

good idea, thanks!

Dan,

Sorry for the delay in getting back to you. Do you still want me to send over an example of my “dispatch” function or have you made progress?

Thanks.

Nolan

No problem at at all! Yes I would love to check it out. I still have 2 functions left on this app I am working on and I was saving that for last anyways.

Hey Dan,

Here is my “onUserCreate” interception point which is fired when a new user registers. The rc.user variable is setup prior to the interception point being executed however you could pass the data in via the interceptData argument.

Also, variables setting is setup within the interceptors configure function, and just extracts some settings from the coldbox config…

public void function configure()
{
// Cache settings within this object to accelerate
// runtime execution. This can save 3-18~ms per lookup.
VARIABLES.SETTINGS =
{
EMAIL = getSetting(‘Email’)
};
return;
}

public void function onUserCreate(Event,interceptData)
interceptionPoint=true
{
var rc = arguments.Event.getCollection();
var prc = arguments.Event.getCollection();

local.renderer = renderService;

// Get new mail object
rc.mail = mailService.newMail().config
(
from = VARIABLES.SETTINGS.EMAIL.from
,replyto = VARIABLES.SETTINGS.EMAIL.replyto
,to = rc.user.getEmail()
,type = “html”
,subject = “Thanks for registering!”
,server = VARIABLES.SETTINGS.EMAIL.server
,port = VARIABLES.SETTINGS.EMAIL.port
,username = VARIABLES.SETTINGS.EMAIL.username
,password = VARIABLES.SETTINGS.EMAIL.password
,priority = “1”
);

// log the notification and get notification hash
// channel 5 = Email, notificationType 1 = User Create Email
local.notificationHash = logNotification
(
channel = 5
,notificationType = ‘1’
,to = rc.user.getUserID()
);

// Create email tokens
rc.tokens =
{
user_firstname = rc.user.getDisplayName()
,user_name = rc.user.getFullName()
,user_email = rc.user.getEmail()
,user_hash = rc.user.getHash()
,email_token = rc.user.getEmail_verification_token()
,notification_hash = local.notificationHash
};

//set email tokens
rc.mail.setBodyTokens(rc.tokens);

//email/user_create contains a switch statement to render the content based on the rc.emailFormat variable. this could probably be achieved using arguments instead of rc vars
//render plain text version
rc.emailFormat = “plain”;
local.plainview = local.renderer.renderLayout(layout=“layouts/Layout.Plain.Email”,view=“emails/user_create”);

//render html version

rc.emailFormat = “html”;
local.htmlview = local.renderer.renderLayout(layout=“layouts/Layout.HTML.Email”,view=“emails/user_create”);

// Add plain text email
rc.mail.addMailPart(charset=‘utf-8’,type=‘text/plain’,body=local.plainview);
// Add HTML email
rc.mail.addMailPart(charset=‘utf-8’,type=‘text/html’,body=local.htmlview);
//send the email
rc.mail_result = mailService.send(rc.mail);

return;
}

private any logNotification(){
//this function just receives and arguments you want to pass in and uses ORM to save them to the database
//used to keep track of all outbound communications
}

That’s basically the Gist of it. Let me know if you have any questions!

Nolan

Looks pretty standard… Just wondering why I would use an interceptor here. Seems like I would do the same thing in say a notificationService component?

Also I log the actual email message to the database. Is there a way to get the actual message body after the tokens have been replaced?

Thanks for the great ideas here!

Hi Dan,

Yep, I’ve contemplated dropping this to a service as well. I have several apps which utilize this dispatcher and each of the interception points are fired at some point within a customers lifecycle, often from two different applications - an API, and a web app. When I started building the app I liked that I could simply register the interceptor once on startup and have access to executing the functions from any part of my app…models, services, etc. this was before the wirebox binders came out. it would make sense to drop to a service but then I would need to inject the service in several more places as opposed to just registering the interceptor. with that being you said you could still use an interceptor, and simply have the interceptor call a service, then you would get the benefit of being able to call it from everywhere, and have the actual notification wrapped up cleanly in a service.

Yes I believe there is a way to get the body. After you set the body tokens try calling getBody() on that var.

Interested to hear anyone’s feedback on this as i’m always looking to improve.

Thanks

Nolan

Dan,

Here an example why I personally would use an interceptor.

Let's say you have a custom interceptor point called "logOff" and 2
interceptors ( e.g. SecurityInterceptor and NotificationInterceptor )
which listen to announcement "logOff".
When a user triggers a logoff/signout event, you can just announce
"logoff". The securityInterceptor will then destroy the user session
and the notificationInterceptor will e.g. send a mail to the
administrator that a user signed out.
Another interceptor e.g. UserEventInterceptor will call a service
which writes a record to the database etc. etc.

Yes, I agree with Nolan: ...with that being you said you could still
use an interceptor, and simply have the interceptor call a service,
then you would get the benefit of being able to call it from
everywhere, and have the actual notification wrapped up cleanly in a
service.....

I personally would always create a service and then YOU should decide
if an interceptor is appropriate.

Ernst

Thanks guys!

I am starting to work on this today and had a quick question. I am going the interceptor route and confused on what to do here. If I had a notificationService interceptor one event that it will listen for is onNewOrder. This will email a user the details about the order they just placed. The email that will go to the admin is quite different. Do I register another event called onNewOrderAdmin and announce 2 events after the order is placed or do I announce 1 event and create 2 different interceptors.

UserNotificationService
AdminNotificationService

Hi Dan,

My opinion is that you’re better off with just a single event, and then register several listeners, one for the customer email, one for the admin.

I say this from experience, we found in an order management app you often end up with all kinds of other mails going out to factory and dispatch locations on given events - broadcasting a chain of different interception points destroys the benefit of an event driven architecture, you may as well just invoke a straight forward method.

I’d say one interception point - multiple interceptors registered to the same point.

Robert

+1 for Roberts thoughts. That is the whole point of the interception points, that you can use them all over your application, whenever a specific event happens and have lots of things listening for the announcement of that event.

Curt

+1 as well.

This worked out well, thanks for the advice guys! Having an issue with the mail service and tokens. Is there anyway I can use some sort of dynamic expression in an email template? Say I have an order and I pass in a body token of paymentMethod = “creditcard”

You can’t do this

<cfif @paymentMethod@ eq “creditcard”>

show credit card info

I need to show / hide stuff based on variables and I can’t think of a way to do it.

No don't think that's possible. Mail/body tokens are parsed (replaced)
when sending a mail.
So do the "logic" outside the mail template and store value in new body token.
You can also create an additional template which depends on in your
example "paymentMethod".

Ernst

Also, remember this cool feature.

If you do renderView() for the body, you can pass into the view additional arguments just for that rendering.

renderView(view=“templates/email”, args={myArg=val, myArg2=val2} )

Then in your views you can use it:

These are view arguments.

Luis F. Majano
CEO
Ortus Solutions, Corp
www.ortussolutions.com

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Social: twitter.com/lmajano facebook.com/lmajano