我々はまだEffの表現力の恩恵を、わずかな一部分しか得られていないのかもしれない

頭の中言語化チャレンジ企画

01.png (46.3 kB)

飲みながらだと、「Effいいんだよぉ!!」と脳死説明しかできないので事前に言語化しておくチャレンジをするマン。

Effのざっくりとした解説は社のブログに書いたので気になる方はどうぞ

tech.recruit-mp.co.jp

Eff導入まで

長いことモナドトランスフォーマー+typed-finalでプロダクションコードを書いていて、型パズルどうにかならんかと思うことがよくあり、当時のチームメンバーだったイッペーと「Typed-finalは1効果-1型パラの抽象化しかできないし、モナトラは効果の組み合わせが増えていくとどんどんつらくなる。良い解決策になりそうなのはFreeモナドあたりで、そこでなんとかゴニョゴニョすれば1対多の抽象化が楽にできるんじゃね?」と漠然と話をしていた。

論文を読んだり勉強会の資料を漁ったりし、2年前の1月にEffを使ったアーキテクチャの構想に至り、メンバーとなんやかんや議論しつつ、2018年6月くらいに担当しているサービスの1サービスをすべてEffに書き換えてリリースした。

リリースはしたが、Effの表現力によって得られる恩恵の大きさを測りかねていた。

シンタックスがフラットになって可読性があがるとか、律速問題の解決とか、計算の順番が変わっても結果が変わらないとかは、more extensible effect誕生のモチベーションそのものなので自明だが、 ソフトウェアの表現力の部分で、もっとどデカいメリットがあると思っていたので、どういうところに一番効くかをぽーっと思案していた。

自分は、研究と実践でいえば実践好きで、研究で得られた理論は実践によって昇華されるはずだと強く思っている。(もちろん研究で得られた理論がないと体系だった実践はできないので双方ともに重要ではあるし、現時点で世界がおいついていないから実践できない理論とかもあるが、細かいことはおいておく)

新しい技術にただ飛びつくのではなく、1実践者として理論の旨味を理解し、どう昇華させるか、枯れさせていくかを考え、すでにある枯れた技術とうまく掛け合わせて行くべきだと思っているので、かなり自明なメリットがないとprodの環境に何かを導入はしないマンである。(dev,stgまではバシバシ入れて検証しまくるではある。要はEffは結構慎重に入れたよという話です🙏 論文や資料を読み込まずに導入するのはおすすめしません🙏)

担当しているサービスで強行軍でアーキテクチャを整えたのも、「マイクロサービスまでは表現力獲得のためには当たり前ラインで、その先の運用やその効率化、インフラストラクチャや、その上に乗るアプリケーションを考えるチームとして充分なメンバーがいるし、サービスの領域もそれくらいやる意義と必要がある」と考えてのものだった。

※手を入れる前の既存の実装は力技の実装が多かったので、問題のレベルが高くないと解のレベルも上がらないとも思っていた。その時はサーバーを4人、サーバー兼インフラ1人で回していたが、新しい人も入り始めていたので、より良い制約と解が必要だった。(このときmodular monolithに考えが至っていたら、最初で7つに分けたサービスは4つですんだが、4つはデプロイ独立性とリソースの最適配分も必要だったので、どうしてもマイクロサービスとして分けないといけなかった)

※当たり前ラインとかいうと戦争が起きそうですが、ある一定以上の複雑性に立ち向かう上では標準装備になっていくものだと思っています😋

そんなこんなあって、みんなの助けがあって(まじで一生感謝)苦労してもってこれた当たり前ラインに、Effという表現力爆増の武器を搭載したので、ソフトウェアアーキテクチャ+Eff, マイクロサービス+Effで素晴らしい次のステップの実践ができる状態になった。

ソフトウェアアーキ + Effで考えたこと

Effの使いみちとして最初に浮かんでいたものは「トランザクションモナド」で、DBの操作をフラットに記述できる事によりslickのDBIO型やFujiTaskのような仕組みの旨味だけを享受できると考えた。これらは便利だが、取り扱う型が増えるためにシンタックスが煩雑になり、受ける恩恵と同等かそれ以上のデメリットがあったのをEffは解消できる。(型が増えるとまじで型合わせが辛い・・・)

あと、説明で使ったのは「メール」だった。 メールは一度発火するともう取り返しがつかない。トランザクションの最後の方に記述するようにはしていたが、トランザクションがコケない確証はどこにもない。 Effであれば処理のどの位置に記述しても、トランザクションの確定後にメールを送信するような表現ができる。

次にうかんだのは、認証、認可、課金で、UseCaseやドメインにしれっと顔を出しやすいこれらの分離と柔軟な表現に使えると考えた。

マイクロサービス + Effで考えたこと

マイクロサービス上で実装する上で、まだ愚直に書く選択しかない「補償トランザクション」、「分散トレース用のrequestId引き回し」をEffのeDSLで表現することによってシンプルに扱いやすく記述できると考えた。

特に補償トランザクションはTryを送る -> Eitherを取り出してRightならConfirm,LeftならCancelという処理が書け、Eitherの効果をまたぎ、継続の恩恵も受けることができ(すべての処理が終わってEitherが返った段階で判定する)、_compensationTranTry_compensationTranConfirmOrCancel のような効果に分けることでインターフェースに補償トランザクションのどのフェーズの処理かも明示できるようになるので、愚直に書くよりは認知負荷や処理漏れなどのバグの心配も格段に減らすことができると思っている。

分散トレース用のrequestId引き回しも、普通に書くとUseCaseまでrequestIdが顔を出すので色んな所を汚染してしまうが、内部通信用の効果のインタプリタに渡せばよいだけになるのでとてもクリーン。

担当しているサービスでは、これ以上の用途は思いついていなかったがマイクロサービス上で楽に正確にわかりやすく記述できるのでこれだけでもテンションが上っていた。

SaaS + Effで考えたこと

SaaSは汎用性と専門性のせめぎあいが必要になると思う。

大型案件などで、どうしても局所的に専用の処理を組んだりする必要がでてくることもあるだろう。

そういう、各社の持つ独自仕様や、マルチテナントをせざるを得ない状況に陥ったときを考えると「アレ、これどうすんだ、人海戦術しか残っていないのか・・?」という気持ちになって憂鬱になる。

だが、「ドメインロジックやら各種Repositoryへのstore処理も効果として定義すれば良いんじゃね?」という考えに至ったときに、Effの懐の深さでなんとかなるかもな?と思った。

そうすれば、どうしても対応しなければならない各社の持つ独自仕様のようなものが出てきた時でも、かなりの部分インタプリタで吸収できる。UseCase同じでデータ構造違うみたいなこともスッキリ表現できる。それで吸収できない部分はUseCaseごと分ければ良い。

マルチテナント化に舵をきるときも「仕様の違いでforkします!!」という地獄のような理由で判断を下すリスクを極限まで減らすことができるな?と気づいた時に「いけるかもしれん」と思った。(やったことないのであくまで思っただけ)

👇コード書きながらそれを思いついて、テンションがあがり酒飲んで訳わからんことをつぶやいていた。(自分自体はアーキテクトってよりはSWE寄りだと思っております)

Screen Shot 2020-09-27 at 4.20.54.png (81.3 kB)

最後のドメイン系IOのEff化はどう書くのかまだ決めきれていない(というか、現時点では必要がない)が、SaaSが死ぬときは、本質の機能提供がミクロな解の提供によって阻害され、ドメインが汚染されて表現力が落ちていくのが要因であることも多そうだなと思っていたので、 それに備えられるのは良いし、この柔軟性こそが競合優位性になりうる。

インタプリタを作りまくって差し替えまくってより良い挙動を検証していくことができるかもしれない。

どうしても対応しないといけない巨額案件の微妙な要件も、専用のインタプリタとControllerでproviderId判定するだけですむかもしれない。

まとめ?

現状、EffはドメインやUseCaseの記述が自然言語に近くなるだったり、シンタックスがフラットになる恩恵や、トランザクションなどの独自定義モナドの恩恵受けることができているが、他にも色々な使いみちがあるかもしれぬ。主に効果のカプセル化とも呼べそうな部分に魅力を感じていて、適用可能な範囲めちゃくちゃ広そうだなー

とか考えると、まだまだ表現力の表層しか使えてないかも? と思うのであった。みんなで使いながら探っていきたい。