はじめに
こんにちは。アプリケーション基盤グループでエンジニアをしている草間です。
SECによるビットコイン現物ETF承認や、ビットコイン半減期の接近などの大きなテーマにより、暗号資産市場は盛り上がりを見せているように感じます。そんな中コインチェックでは、「新しい価値交換を、もっと身近に」のミッションに基づき、2024年に入ってからも実現損益チェッカーやCoincheckリワード、Coincheck OnRampなど、数々の新しいサービスをリリースしてきています。
コインチェックでは2014年に暗号資産取引サービスを開始して以来、様々なサービスが誕生してきました。多くのプロダクションコードが生まれ、段々と規模の大きいコードベースとなっていくに連れて、成長に伴う技術的負債も積まれてきているのが現状です。
私が在籍するアプリケーション基盤グループは2023年9月に発足しました。各事業部の開発速度、品質、生産性を向上させることをミッションに掲げており、エンジニア全体の横断的な課題解決や改善活動に取り組んでいます。
今回は現在取り組んでいる施策の1つとして、認可ロジックのパッケージ化についてご紹介させて頂きます。
認可ロジックをパッケージ化しようとした背景
コインチェックでは、お客様に安全にサービスをご利用していただくため、お客様のステータスに応じて都度取引可否を判断する必要があります。取引可否の判断は、機能へのアクセス権限(認可)の判定と言えるため、これを認可ロジックと呼んでいます。多くのサービスが生まれる中で、各ドメインのビジネスロジック内で独自の認可ロジックが散在する状況となってしまいました。
さらにコインチェックのサービスは、一つ一つの認可ロジックが複雑であるという特性もあります。主に法令要件などを理由に、本人確認情報などの細かい状況について様々な判断が必要となっており、お客様に関連付く操作権限を参照するだけのようなシンプルな認可判定ではありません。
複雑な認可ロジックが散在していることで全体の見通しが悪くなり、新しいサービスをリリースする際に必要な認可ロジックの確認工数が増えたり、重複してステータスを判断するコードが生まれるなどの問題がありました。
そこで認可ロジックを集約し、認可判定を一律に担う仕組みの構築を進めることにしました。
上図のように、問い合わせに応じて取引の実施可否を返却する認可パッケージを提供することで、ビジネスロジック側では個別にステータスを判断する必要が無くなり、認可パッケージの戻り値だけ判定すれば良い構造となります。
packwerk導入に至るまでの経緯
認可パッケージを作るにあたって、どのような構造が良いか検討を行いました。
認可パッケージは、共通部品としての新しいモジュールという色合いが濃いので、パッケージ外からのIFは最小限に留め、依存関係が少ない疎結合の形にしておくべきと考えました。
コインチェックのバックエンド部分は、現状モノリシックなRailsアプリケーションとなっており、複雑なロジックを疎結合に作る仕組みがありません。
マイクロサービス化の選択肢は、認可判定に使うデータの切り離しが現段階では難しいことから今のシステムにマッチしないと判断しました。一方packwerk を導入することで、複雑なロジックのIF を切り出しやすいのではと考えました。
packwerkを利用すると、何らかの要因でパッケージ化したコード類をコア側に戻すといった対応を低コストで行える点や、パッケージ間の依存関係が見える点、プロダクションのパフォーマンスに影響を与えない点が利点として挙げられます。
上記を踏まえて、packwerkを使ったモジュラーモノリス構造で作成することに決定しました。
packwerkについて
packwerkは、ShopifyがOSSとして公開している、Railsアプリケーションのモジュール化を支援してくれるgemです。具体的には、パッケージという単位に分割して配置されたコードに対して静的解析を行い、パッケージ間における依存関係をチェックするコマンドラインツールを提供しています。
チェックした依存関係は、packwerkの設定に応じて下記のような対応を選択することができます。
- パッケージの新たな依存関係として設定する。
- パッケージ間の処理を見直し、依存関係を解消する。
- 依存関係の設定、または解消を保留とし、記録する。
今回はパッケージ化の初回導入という状況なので、特に細かい依存関係の設定は行っていません。
packwerkにあわせて、関連gemもいくつか導入したのでご紹介します。
packs-rails
packs-railsは、パッケージ配下のコードを自動的にRailsアプリケーションとして動作するようにしてくれるgemです。packwerk単体では手作業でconfig修正などが必要だった箇所、例えばzeitwerkのautoload_pathsへの設定処理などが不要になり、パッケージ配下にコード類を配置するだけですぐにモジュラーモノリスの構成が作れます。
パッケージとして認識されるディレクトリはデフォルトではpacks
ディレクトリ配下なのですが、これはpacks-railsが依存しているpacks gemの設定でカスタマイズができます。例えばpacks.yml
に下記のような設定をすると、packs/domains
とpacks/components
がパッケージのルートディレクトリとして認識されるようになります。
pack_paths: - "packs/{domains,components}/*"
パッケージのルートを複数指定することができるので、パッケージを分類分けする場合などの用途が考えられます。
packwerk-extensions
packwerk-extensionsはpackwerkの拡張チェックを担っているもので、プライベート領域へのアクセスを検知、制限を目的に導入をしました。
このgemでは、Rubyファイルの先頭にマジックコメントのような形でパブリックアクセスを許可する旨を記載することで、他パッケージからのアクセスを許容することができます。ちなみにこの仕組みは2023年10月のアップデート(v0.1.9)で取り入れられたものです。
# pack_public: true class AuthorizeService ・・・ end
アップデート前は、パッケージ配下にprivateディレクトリを切って、更にその下にRailsディレクトリの構成を配置するなどが必要だったのですが、ディレクトリ構成を汚くすることなくパブリック領域の設定ができるようになりました。
この仕組みを使うことで、パッケージ外から認可パッケージ内の不適切なロジックが直接呼び出されることが無くなり、一律な認可判定の担保に繋げることができました。
上述のpacks-railsとpackwerk-extensionsは、いずれもgustoが取り組んでいるrubyatscaleという、Ruby/Rails開発組織のスケールに役立つツール群を提供するエコシステムとして提供されています。
各gemの誕生背景など記載されているブログもありますので、こちらもご紹介しておきます。
パッケージ化の結果と今後
完成した認可パッケージは、下記のような構成となっています。
packsディレクトリ配下にauthorizationパッケージが格納され、パッケージ配下はRailsのディレクトリ構成に則った形となっています。
認可パッケージの構築は完了しましたが、分散している既存の認可ロジックを剥がして、判定を認可パッケージに集約する作業は現在も順次実施中です。また剥がし作業後は、認可判定を認可パッケージを介さずに行うコードを生み出さないようなチェック処理の検討なども予定しています。
今回認可ロジックをパッケージ化したことで、影響範囲の明確化や仕様の簡略化へと繋がり、この先認可に関連する改修を行う際にも生産性が向上すると見込んでいます。
おわりに
認可ロジックのパッケージ化の取り組みについてご紹介させて頂きました。
今回のpackwerk導入に伴い、モジュラーモノリス構造を検討する上で、Timeeさん、noteさん、hacomonoさんの発表資料を先行事例として参考にさせて頂きました。
- Timeeさん:RailsでModular Monolithを選択された御社に質問したいN個の疑問 - Speaker Deck
- noteさん:モノリシックRailsアプリケーションを モジュラモノリスへ移行している noteの事例 - Speaker Deck
- hacomonoさん:hacomono Platform Engineering Showcase #2 - Speaker Deck
直接交流の時間も頂くことができ、発表資料と併せて大変勉強させて頂きました。ありがとうございました。
今回ご紹介したpackwerkを利用することで、モジュラーモノリス構造でのパッケージ化のモデルケースを作ることができました。今後は、認可以外の課題についてもpackwerkの適用を検討、推進していき、開発速度、品質、生産性の向上へ繋げていければと考えています。
なお、コインチェックでは一緒に働くエンジニアを募集中です!詳しくは求人ページをご覧ください。