カジュアルな技術ノート

小難しい技術のお話をできるだけわかりやすく...

メールの事故から考えるメールライブラリとテンプレート

先日の jflute さん勉強会で MailFlute のソースコードリーディングをやりました。
今回はメールライブラリとテンプレートのお話です。

java でメール送信するときの手順を考えると、

  1. Velocity などのテンプレートエンジンを使用してメール本文を完成
  2. Java Mail などのメールライブラリを使って送信

という流れが一般的ではないでしょうか。
そしてここで使うテンプレートやライブラリのことをしっかりと考えたことありますか?
僕はこの勉強会受けるまで一切考えたことなかったです(笑)。

実はメールの特性を考えると、めちゃくちゃ重要だなと感じたので紹介します。

この記事で扱うこと

  • メールの特性
    メールは事故りやすく、事故るとやばいです。
    その理由を取り上げていきます。
  • メール事故を防ぐ仕組みと工夫
    メール事故を防ぐための仕組みや工夫を盛り込んだライブラリとして MailFlute をみていきます。

メールは事故りやすく、事故るとやばい

基本的に web アプリケーション開発をしている現場ということを想定していますが web アプリケーションで生じるバグよりもメールでの事故の方がシリアスであることが多いです
その理由をいくつか考えてみました。

  1. 一度送信すると取り消すことができない
    web アプリケーションで生じたバグはアプリケーションを修正することで改修することができます。
    しかしメールは送ってしまうと取り消すことができません。
    送信相手の名前を間違えて送ってしまったとしましょう。
    やることはバグ修正ではなく、謝罪のメール作成ですね。。
  2. 間違いに気づきにくい
    メールは間違いに気づきにくいです。
    手動で送ったメールならまだしも、アプリケーションが送信したメールを送り手が読むケースはあまりないでしょう。
    そして web アプリケーション上のバグのように多くのユーザーの目に触れることもなく、間違いメールを読むのは受信者のみということが多いです。
  3. ステークホルダーが増え、間違いが生じがち
    メールテンプレート作成するとき、その本文を書くのはセールスなど開発者でない場合が多いです。
    通常テンプレートファイルにはテンプレート変数を埋め込みますが、開発経験がない人は断然間違える可能性も高くなります。
    このように開発者以外のステークホルダーが絡むことで通常のアプリケーション開発と比べるとバグの混入率が高くなるということができるでしょう。

こう考えると恐ろしいですね。。。
メールの事故を防ぐための仕組みや工夫が欲しくなります。
その一例として、メールライブラリやテンプレートで事故防止するということを考えていきましょう。

MailFlute の紹介

ここまであげたようなメール特有の怖さを回避する仕組みや工夫を盛り込んだライブラリとして MailFlute を紹介します。
MailFlute はメールライブラリであり、かつテンプレートエンジンも包含しています。

dbflute.seasar.org

↑から抜粋ですがメール送信時の java コードは次のようなものになります。

WelcomeMemberPostcard.droppedInto(postbox, postcard -> {
    postcard.setFrom("from@example.com", LABELS_OFFICE_MAIL);
    postcard.addTo("to@example.com");
    postcard.setMemberName("sea");
    postcard.setBirthdate(birthdate);
    postcard.addReplyTo("replyto@example.com");
});

MailFlute は DBFlute ライクなコードでメールが送信できるのが特徴です。
いくつか特徴をみていきましょう!

テンプレートごとにクラスを自動生成

上のコードでは WelcomeMemberPostcard が自動生成されたクラスとなります。

WelcomeMemberPostcard.droppedInto(postbox, postcard -> {
    ...
});

テンプレートごとにクラスが生成されるので テンプレートファイルのパスを開発者が指定する必要がありません
間違ったテンプレートが選択されることを防ぐことを目的としているとのことです。

テンプレート変数ごとにメソッドを自動生成

取り上げたコードでは memberName や birthdate をテンプレート変数として指定しています。
MailFlute ではテンプレート変数ごとに固有のメソッドで値を指定するのが特徴です。

WelcomeMemberPostcard.droppedInto(postbox, postcard -> {
    ...
    postcard.setMemberName("sea");
    postcard.setBirthdate(birthdate);
    ...
});

これによって テンプレート変数指定のタイポを防げる、タイプセーフであることから間違った値を指定しにくくなる ことが期待できます。
比較として Velocity だと次のようなコードとなり、MailFlute に比べると間違いが検知しにくくなります。

Map<String, Object> contextMap = new HashMap<>();
contextMap.put("memberName", "sea");
contextMap.put("birthdate", birthdate);
...

コメント必須なテンプレート

これはオプションによって設定できる機能ですが、テンプレートファイルにコメントを必須にすることができます。

/*
 [新規会員メール]
 会員登録を申し込みした時に送信されます。
 注)本登録時のメールではありません。
*/
subject: Welcome to your sign up, /*pmb.memberName*/
option: +html
-- !!List<Integer> seaList!!
>>>
Hello, /*pmb.memberName*/

コメントは /* ... */ にあたるところですね。
ステークホルダーが多いメールテンプレートだと、認識に齟齬が生じやすくなると思います。
だからこそメールに関する情報や注意事項をコメントに残しておくことが重要という思想から生まれた機能です。

まとめ

今回みてきた MailFlute の機能一つ一つはとても小さな工夫です。
しかし事故を防ぐために必要なこともまた一つ一つの小さな工夫の積み重ねだと思います。
そしてできるならそのような工夫も仕組みとして導入した方が現場のストレスがより少なく安全な状態を生み出すことができると思います。
そんな事例の一つとして、メールライブラリとテンプレートについて参考にしていただけると嬉しいです。