Part 1, Planning
Building a system to bulk import emails from your postmark server(s)
A bit of background
We use Postmark at Seraphin to handle our transactional e-mails.
We recently wanted to try and overhaul the way we work with our emails, but we have quite a few of them (a bit more than a hundred).
We wanted a tool to keep track of our changes, test them, and upload them easily.
This tool should be able to:
- Import all the emails from Postmark
- Test either a single email (with a layout) or all emails
- Update all emails to Postmark
This article will explore the big picture of the project's initial considerations by discussing the strategy and our code's architecture.
Strategy
When thinking of the overall strategy, we are interested in the big picture of how our project/product must behave. In our Postmark example:
- We need to be able to bulk import all emails
- We need to be able to test the emails easily
- We need to be able to upload the modified emails to Postmark
Knowing this, we already have some choices and research to make. Does something already exist that will allow us to do these three tasks easily? (removing the need to do anything).
The answer to this is: kind of, mail mason will do some of what we want, namely testing and uploading. But it has a pretty big drawback IMO, and it would force us to change how our emails and files are structured. This does not preclude the use of this tool, but it does lead me to investigate further.
Can I quickly (more easily than changing how our emails are structured) make a tool to handle the three options? Is there some support for what I want to do? While answering this question, I saw: Postmark Api and Postmark Gem which suggested that it should not be too difficult to do.
So it looks like we can commit to building a quick tool, but how should it look or feel?
The tool will mainly be used by developers, so we probably can get away with a quick and dirty CLI. Knowing that my intended target uses ruby, I am pretty safe with a ruby program.
Architecture
When thinking about the 'big picture' architecture of a project, even more one that is starting from scratch, here are the areas we will look for:
- How to structure/store the data
- How to conceptualize and model reality (Assuming OOP)
How to structure/store the data
Here is something that should not shock you, but it probably will: A database can be composed of a folder with HTML files and a big yaml 'index' file. And you know how I know that? Because our project uses that as a Database. And I will expose to you now why it makes sense.
On the one hand, the HTML files need to be easily accessible by the people changing them. By having a template_name_template_id.html file per template, we manage to have. On the other, a lot of metadata (alias, layout, text_body, etc.) needs to be associated with a template. By having a metadata.yml file, we can associate that information with each template.
How to conceptualize and model reality
When working on something like this, I start with a very rough sketch of what I am trying to do. Usually, on the nice whiteboard I have in my office:
Yes, I know, my calligraphy might make it more of a challenge than it has to be for me.
Here is a good sketch of what the architecture of our project looks like (I made some changes compared to the rough sketch on the whiteboard while working on the project):
We will get into the details of these classes as we progress on the series, but the rough idea is:
We have two 'models' representing two different email types, which might be counterintuitive. You might say that there should be only an Email or Template class. But as I see it, these two classes are different and have different responsibilities:
As we will explore in later instalments, InternalTemplate
represents the email in our internal DB. On the other hand, ExternalTemplate
represents the template in Postmark. These are, in my opinion, different and should be conceptualized differently.
Moreover, the responsibility of InternalTemplate
is to either send data to Postmark, whereas ExternalTemplate
's responsibility is to receive data from Postmark.
The services are where the code to interact with Postmark is abstracted.
Conclusion
Planning is essential; even for a project that seems relatively trivial, I always take the time to create a rough sketch of what I am trying to accomplish.
This planning allows me to see where the problems might lie in the future and gives me an excellent foundation to work on when I start coding the particulars.
This does not mean that the plan is static, and I amend it throughout the course of a project.