認証と認可と課金とコアドメインを分離したシステムは勝てるという話

自分が複数のシステムの開発を経験して得た確信として、「認証と認可と課金とコアドメインの分離がめちゃくちゃ重要である」というものがあるので、コレを整理してアウトプットしていく

分離するモチベーションとは

Microservice文脈でいうと、デプロイ独立性だったり、リソースの最適配分だったり、障害の局所化だったり、開発組織とのマッピングだったりがメリットとして語られることが多い。

だが、ここで取り上げたいのは戦術的DDD的観点でのコンテキスト分離の有用性である。

※ちなみにコンテキスト分離のみであればモジュラモノリスだけで実現可能。

戦術的DDD的観点での関心事の分離によるメリットとは

コンテキストが分離されていることによって、境界をまたぐ際に「このI/Fは正しいのか?」を都度考えることを強制することができる。 境界がなければ意図しない密結合を生みやすくなってしまう。

もちろん、境界を超えるためのボイラープレートとトレードオフであるが、ドメインをシンプルに保つためのボイラープレートは、常識的範疇の量であればドメインがシンプルになる効果の方が大きすぎるので、開発速度や保守観点、開発する機能のクォリティに寄与し、十分ペイすると考えている。

処理を書く際に境界を意識し、都度「この境界は正しいのか」「境界またぐ時、ここだけ異常にめんどうじゃね?」と考える機会はとても重要で、明確な境界がなければ意図せず不必要な密結合を生み出してしまいがちなところを強制的に考えさせる機会を設けることで、ある種モデルの洗練に強制力をもたせることができる。

(最近自分が自社で実装した)AuthzIOのPermissionReason(権限付与理由)などは「境界またぐ時、ここだけ異常にめんどうじゃね?」から生まれたものだったりする。(例がわかりにくいかもだが、情報の正引き,逆引き両方を効率良く行えるデータ構造を考える機会になった。)

ただ、この部分は定点観測して定量的に差が出せるものでもないので、「メリット」として捉えるのに時間がかかる部分かもしれない。

個人的には1サービス内のHttpAdapter,SecondaryAdapter,UseCase,Domain等の横の境界の有用性と同じくらい有用で、一定の複雑度を持つことがわかっているシステムであれば最初から切るべき境界だと確信を持っているし、モデルの正しさ、汎用性、使い勝手の良さで勝負するシステムではなおさら重要である。(なので縦のドメイン境界を切るモジュラモノリスやマイクロサービスをやっている)

認証と認可と課金とコアドメインの分離は何が良いのか

では、何を分離するのか。という話になるが、何を分離するのかはむずい(おいおい)

ただ、その中でも認証、認可、課金とコアドメインってのはなんの疑いもなく1000%分離すべきものである。

コアドメインの中をどう分離するのかはやりながら考えるしかないが、こいつらはとりあえず分離しておけばサービス成長に伴う成長痛を最低限に抑えることができる。

サービス成長に伴う成長痛とは

急速な機能開発によりシステムに歪な状態が埋め込まれ、それを解消するためにアーキテクチャ変更やリファクタリング、データ移行等が必要になるような状況を指す。

新規機能開発の一時停止や停止メンテを厭わなければ後は馬力でなんとかなるではあるが、相当の馬力を要するのでその馬力はもっと有用な、素敵な機能の開発などに回したいし、ここに馬力を発揮するのはきつい。

当たり前の状態にするための馬力と素敵機能を開発するための馬力は後者のほうが心にも優しい。

成長痛を最低限に抑えられる理由

小規模な時にばーっと書いちゃって密結合になりやすい筆頭の関心事が認証、認可、課金とコアドメインで、分離するのにメチャクチャな労力を要する。(分離なんてきれいな言葉ではなく、もはや引きちぎるに等しい。)

ここをスキップさえしてしまえば後はコアドメインの洗練や分離に注力できるし、ここの分離さえできていれば、かなりの要件に迅速かつ柔軟に対応ができるので無駄な痛みを抑えられると考えている。

開発を止めるレベルでのリファクタリングや停止メンテを必要とする変更は、自分の経験上はすべて認証、認可、課金周りだった。

これらはコード上だけでは済まない外部要因が絡むため、整合性を保つためにかなりの労力を要する。

  • 課金ならPaymentGateway側のデータと自DBに持つデータの整合性
  • 認証ならフロントとJWTのやり取りだったり(全員強制ログアウトさせてOKなら何も考えなくてOKだが・・・)、FirebaseのようなIDaaS
  • 認可なら認証,課金,コアドメインに食い込みまくってる

等など。

引きちぎると形容したように、整合性のために完全には分離できないことが多い。 引きちぎり元に残った患部とはシステムの寿命が続く限りメンテし続けるか、またまた停止メンテを行い連携先と自分らのデータに破壊的な変更を加えて整合性を保つかだが、後者の判断を下すにはかなりの合理性がないと労力に見合わないので大抵はメンテし続けることになる。

コアドメインであれば、データは自分たちのDBに入っているし、入るまでの経路も全て自分たちでハンドリングしている。 変更を加えるのは比較的現実的である。

また、サービス成長に伴ってお金に余裕がでてきたときにマーケティング系の施策をうつことが多くなる。 その時点で分離してないと「そんな要件みたせねーよ」といった機能を求められる。(キャンペーンうつぞ!最初から課金してくれてる人に感謝のほげほげ!等等)

Dropboxの「最初期課金ユーザー永遠無料」みたいな施策は、認可、課金が分離してない状態で実現するのは地獄。

※課金のデータを日付で漁って最初期課金ユーザーだと判定できれば●●を許すみたいなコードが各所に散るか、最初期ユーザーリストみたいなのをもって毎回参照する等。キャンペーンの種類が増えるたびにユーザーリストを作るのは現実的ではない。

分離されていれば「最初期課金マン」みたいな権限用意して権限チェックのときに考慮するだけで済む。

施策を最適なタイミングで最速でうてるのはかなり強いと思う。

まとめ

システムにおける表現力は、あっちを立たせればこっちが立たずみたいなバランスが存在していて、ある要件Aに沿って実装しすぎると後から出てきた要件Bへの対応が難しくなる。みたいな力学がある。コンテキスト分離はこのバランスを保つためにとても重要で、バランスを保つことでシステムの表現力を失わずに済む。

早い段階で取り組むことにより成長痛を抑えつつ、分離による表現力を高めることで今後起こり得る要件に柔軟に対応しつつ、痛みなく爆速で成熟していけるので結果勝てると思う。という話でした。