Skip to content
David García edited this page Apr 15, 2016 · 11 revisions

MoonMail Sender

Sender takes care of email composing and sending.

Features

Sender service’s duties are, primarily, composing emails and sending them. It’ll read emails to be sent from the SendEmail SQS queue, it’ll compose them (which involves parsing liquid tags) and send them through SES.

Dependencies

  • list: it sends an SNS notification with the campaign and recipient data to the PrecompileEmailTopic for every recipient contained in a campaign to be sent.

Architecture

It’ll have two lambda functions, precompileEmail, that will compose emails and place all the needed info in a SQS queue specific for the given user, and sendEmails, that will send a batch of emails through SES. There will be as many SQS queues as users, and each of them will have a CloudWatch alarm that triggers a SNS message to the SendEmailsTopic, so that it notifies sendEmails that has to send messages from that queue. It'll get a batch of messages and invoke itself when it finishes, in case the queue is not empty.

System Messaging

This service publishes and reads messages from the following SNS topics:

  • PrecompileEmail
  • SendEmails

And SQS queues:

  • SendEmail: actually, this won't be a single queue, but there will be as many queues as users (they'll be named after their IDs, so that user can be easily identified) and one more for all the users in the free plan.

PrecompileEmail Topic

Lists service publish messages to this topic, including recipient's email and metadata, and the campaign info, for each recipient in a campaign.

Canonical Message

{
  "userId": "...",
  "sender": {
    "region": "...",
    "apiKey": "...",
    "apiSecret": "...",
    "ratePerSecond": "...",
    "emailAddress": "...",
    "fromName": "..."
  },
  "campaign": {
    "id": "...",
    "body": "...",
    "subject": "..."
  },
  "recipient": {
    "email": "...",
    "metadata": {
      "name": "...",
      "surname": "..."
    }
  }
}

Messages flow

  • Lists service will push a notification, that will be fetched by precompileEmail function.
{
  "userId": "...",
  "sender": {
    "region": "...",
    "apiKey": "...",
    "apiSecret": "...",
    "ratePerSecond": "...",
    "emailAddress": "...",
    "fromName": "..."
  },
  "campaign": {
    "id": "...",
    "body": "...",
    "subject": "..."
  },
  "recipient": {
    "email": "...",
    "metadata": {
      "name": "...",
      "surname": "..."
    }
  }
}
  • precompileEmail parses subject and body liquid tags and pushes the message to the SQS queue named after the user ID.
{
  "userId": "...",
  "sender": {
    "region": "...",
    "apiKey": "...",
    "apiSecret": "...",
    "ratePerSecond": "...",
    "emailAddress": "...",
    "fromName": "..."
  },
  "campaign": {
    "id": "...",
    "body": "<parsed_body>",
    "subject": "<parsed_subject>"
  },
  "recipient": {
    "email": "..."
  }
}

SendEmails Topic

There will be a Cloud Watch alarm set up so that it triggers an SNS event when the queue is not empty. sendEmails function is subscribed to the topic, and starts sending emails from the given queue.

Canonical Message

This is a simplified version of the actual message, generated by Cloud Watch, but the important info is only the queue name.

{
  "AlarmName": "queueNotEmpty",
  "AlarmDescription": "queueNotEmpty",
  "AWSAccountId": "862946620411",
  "NewStateValue": "ALARM",
  "StateChangeTime": "2016-02-11T15:16:41.224+0000",
  "Region": "US - N. Virginia",
  "OldStateValue": "OK",
  "Trigger": {
    "MetricName": "ApproximateNumberOfMessagesVisible",
    "Namespace": "AWS/SQS",
    "Dimensions": [
      {
        "name": "QueueName",
        "value": "test-queue"
      }
    ]
  }
}

Messages flow

  • sendEmails gets a notification from the topic.
{
  "AlarmName": "queueNotEmpty",
  "AlarmDescription": "queueNotEmpty",
  "AWSAccountId": "862946620411",
  "NewStateValue": "ALARM",
  "StateChangeTime": "2016-02-11T15:16:41.224+0000",
  "Region": "US - N. Virginia",
  "OldStateValue": "OK",
  "Trigger": {
    "MetricName": "ApproximateNumberOfMessagesVisible",
    "Namespace": "AWS/SQS",
    "Dimensions": [
      {
        "name": "QueueName",
        "value": "test-queue"
      }
    ]
  }
}
  • sendEmails gets a batch of emails from the queue and sends them asynchronously. If the queue it's not empty, it'll invoke itself again, providing the queue info.

SendEmail Queue

precompileEmail function parses subject and body liquid tags and pushes the message to the queue.

Canonical Message

{
  "userId": "...",
  "sender": {
    "region": "...",
    "apiKey": "...",
    "apiSecret": "...",
    "ratePerSecond": "...",
    "emailAddress": "...",
    "fromName": "..."
  },
  "campaign": {
    "id": "...",
    "body": "<parsed_body>",
    "subject": "<parsed_subject>"
  },
  "recipient": {
    "email": "..."
  }
}