🏔️

マルチテナントサービスにおけるカレンダー管理について

2021/12/25に公開

背景

昨年は複雑な営業日カレンダーを取り扱うための、日付モデルを紹介しました。

複数の営業日カレンダーを扱う業務のための日付モデルの紹介

この日付モデルのおかげで、エンジニアを含む全てのステークホルダーに対して、要件定義や設計の過程において営業日カレンダーを誤解なく伝達可能となりました。

しかしながら、この日付モデルはあくまで営業日カレンダーの中身(いつが営業日か)を定義する実装の世界の言語(モデル)であり、ユビキタス言語(共通言語)ではありません。

例として、注文を受付可能な日=受注可能日を考えましょう。
受注可能な日が、「JPX開場日かつ米国開場日である日」と定められていたとします。
前回の記事に従うと、受注可能日の営業日カレンダーは jp&us と表現されます。
例えば2021年12月25日が受注可能日かどうかを知りたいという業務では、日付モデルを使うと以下のようなやりとりが行なわれます。
この時、受注可能日をユビキタス言語、jp&usは受注可能日を抽象化、汎化したモデルにあたります。

このように、実際の業務においては、上述のようにユビキタス言語を使ってやり取りされるため、ユビキタス言語からモデルへの変換が必要です。
自社サービスの運営というコンテキストでは、ユビキタス言語とモデルの紐付けは1対1に決まるため、ハードコードして管理すれば十分でした。

しかしながらマルチテナントのサービス提供を考えた場合には、同じ業務を行なっていて同じユビキタス言語である「受注可能日」を使っていたとしても、以下のように企業によって紐づくモデルが異なることがあります。

そのため、実際にマルチテナントのサービスを提供するには、単に日付モデルを準備するだけではなく、ユビキタス言語とモデルとの紐付けを動的に管理する仕組みが必要となりました。

そこで本稿では、日付モデルと営業日との紐付けを動的に管理するために、マルチテナントに提供するサービスで採用したカレンダー管理の機構について紹介したいと思います。

定義

まず準備として、定義を行ないたいと思います。

用語 定義
営業日カレンダー 日付の集合
営業日 営業日カレンダーに含まれる日付
ドメインカレンダー 冒頭で紹介した「受注可能日」のようなユビキタス言語と、一意にひもづく営業日カレンダーのこと
パブリックカレンダー jp(JPX開場日)やus(米国開場日)のような公式な営業日カレンダーのこと、取引先のような特定企業の営業日なども含まれます
カスタムカレンダー ドメインカレンダーとパブリックカレンダーに含まれない営業日カレンダー、例えばjp&us, `jp
カレンダーID 営業日カレンダーを一意に特定するID、例えばJPX開場日ならjp、米国開場日ならjp&usとなります

稼働日判定の流れ

準備が終わったら、実際にドメインカレンダーを使った稼働日判定の流れを追ってみましょう。
具体例を元に流れを追っていく方が理解が進みやすいかと思いますので、例えば「受注可能日の翌日」(以下、稼働日と呼ぶ)に注文伝票を印刷する処理を行なうバッチを考えましょう。
なお受注可能日の中身は「JPX開場日かつ米国開場日である日」=jp&usとします。

まずはパブリックカレンダーの登録が必要です。
パブリックカレンダーは全てのカレンダーの元となるものです。

受注可能日の中身はjp&usですが、そもそもJPX開場日=jpや米国開場日=usの情報がないと、受注可能日を計算する事はできません。
パブリックカレンダーは自動生成は出来ませんので、これだけは手動管理したりインターネットなどのデータソースから集めたりする必要があります。

次にカスタムカレンダーを作成します。
パブリックカレンダーが用意されたので、 jp&usが計算可能になりました。
jp&usを計算して、カスタムカレンダーとして登録します。

最後にドメインカレンダーの作成です。
jp&usを元に受注可能日を作成します。(受注可能日のカレンダーIDは、orderable_dateとします。)

これでバッチは登録されているorderable_dateを使って、今日(以下、Tとします)が稼働日かどうかを判定出来るようになりました。

この例では、バッチの稼働日 = 「受注可能日の翌日」でした。
バッチは以下の流れで今日が稼働日であるかどうかを確認します。

  1. \tau=f(T)=T\_c-1を計算
  2. \tauorderable_dateに含まれているか確認

2が真であればバッチは稼働日、偽であれば非稼働日となります。

今回の稼働日は「受注可能日の翌日」なのでf(T)=T\_c-1でしたが、もし「受注可能日の前日」であればf(T)=T\_c+1のように変えることで対応可能です。
どんな業務要件でも対応可能かどうかは分かりませんが、自身の経験上では、適切にドメインカレンダーを定義することで、ほとんどのケースでシンプルに稼働日が表現可能になる印象です。
特にほどんどのバッチの稼働日が少数のドメインカレンダーの前後や当日となるような、大規模なバッチシステムの場合にとても有効でした。

また冒頭でもお伝えした通り、マルチテナントサービスでは同じドメインカレンダーであっても中身が異なる場合があります。
この場合も、ドメインカレンダーを元に稼働日判定を行なっていれば、各バッチには手を加えずにorderable_dateのソースを切り替えるだけで、テナントごとの差異が吸収可能となります。

おまけ

以上で稼働日判定を行なう流れを見てきました。
が、カスタムカレンダーって必要なの?、という疑問が湧いた方がいらっしゃるかも知れません。
この記事では、カレンダーサービスとカレンダーを使うサービスが別である、以下のような構成を前提としていました。

この構成の理由は以下の感じです。

  • ユビキタス言語はコンテキストごとに異なる = ドメインカレンダーはサービスごとに変わりうるもの
  • パブリックカレンダーは一つだけ存在して欲しい(二重管理は避けたい)

この構成におけるカスタムカレンダーは、複数のコンテキストに共通するコンテキスト(メタコンテキスト?)におけるドメインカレンダーというイメージになります。
ですので、単一のサービスの場合にはカスタムカレンダーはおそらく不要ですし、複数サービスが存在してもサービス間のコンテキストの共通部分が少ない or 無ければカスタムカレンダーは不要になるのではないかと思います。

この辺りは、ご自身が設計される際にご検討、取捨選択してみて下さい。

おわりに

本稿では、マルチテナントサービスにおけるカレンダー管理について、ご紹介をしてみました。
最近はVertical SaaSが増えているそうなので、マルチテナントでカレンダーを取り扱うサービスも多いのではないのかなと思います。
もし皆様のカレンダー設計の際に少しでもご参考になれば幸いです。

もしご指摘、ご質問等ありましたら、ぜひTwitterでご連絡をお願いします。

Discussion