Taro L. Saito
Airframe
Published in
7 min readOct 20, 2018

--

AirframeによるScalaプログラミング:「何ができるか」から「何を効果的に忘れられるか」を考える

TD Tech Talk (October 17, 2018)で、Scala開発のための軽量ライブラリコレクションであるAirframe (https://wvlet.org/airframe) について紹介しました。

当日の発表スライドはこちらから:

要点その1: MessagePackを活用すると汎用的なデータ変換が実現でき、データフォーマットごとにオブジェクトマッパーを実装する手間が省けること。スライド中に紹介していますが、応用範囲はデータの変換、保存、転送だけでなくHTTPサーバーの実装と非常に広いです。

MessagePackを使う利点は、以下の3つ:

  • 様々なデータフォーマットに対応できる
  • コンパクトなバイナリ表現
  • Self-describing format (データ自身が型を持っている)ため、種々のフォーマットからオブジェクトへのマッピングをschema-on-readのテクニックで実装できる

また、オブジェクトデータをデータフォーマットを気にせずに手軽に保存できるというのは、計算の再利用、プログラムの再開などを可能にしてくれるので、複雑なデータ処理において強力な武器になります。

要点その2: AirframeにはDependency Injection (DI)のためのライブラリがありますが、DIを理解するには、「何ができるか」ではなく、プログラミング中に「何を忘れることができるか」が大切です。

Dependency Injectionは「何ができるか」という観点で考えると非常に難しい概念です(DIライブラリの機能比較表も用意しましたがこれだけでAirframeの便利さを実感するのは難しいでしょう )。ここで発想を転換して「(プログラミング中に)何を効果的に忘れることができるか」を考えると、抑えておくべきポイントは以下に挙げる3つだけで済みます。

1: オブジェクトの構築方法を忘れる

まず、サービスを構築する際に複数のオブジェクト(モジュール)を組み合わせるのが通常のプログラミングですが、プロダクションとテスト時ではモジュールの構成が違うのが通常です。

上図のAやBを実装する際に、直接的な依存関係(direct dependency: DBClient, FluentdLogger)だけでなく、間接的な依存関係(indirect dependencies: ConnectionPool, HttpClient, DB, など)まで考慮すると、プログラミング中に大きなワーキングメモリーを必要とします。例えば、毎回手で依存関係を記述する(hand wiringと言います)と以下のようなコードを書く必要があります。

一方、Airframeを利用すると、Aの構築方法(design)を分離し、フレームワーク側に自動的にwiringを任せることができます。

一部のモジュールをテスト用に変更するのも簡単です:

こうすることで、プログラマはAやBの実装のみに集中することができます。これはインターフェースによる実装の分離という意味だけだと、プログラマにとって常識ではありますが、モジュールの種類やコンフィグオブジェクトの種類が数十〜百近くになると、次第にオブジェクトの受け渡しが面倒になり、インターフェースを最小に分離するのすら困難になる問題が起こります。(例えば、アプリケーションの全コンフィグを、AやBに渡さないとDBを設定できない、など。当然、通常のDBとテスト用のIn-Memory DBでは必要なコンフィグの種類も異なりますし、DBの設定そのものは、A, Bのロジックには関係ありません)

2: リソースの管理順(FILO Order)を忘れる

データベース、HttpClientなど、起動処理、終了処理が必要なモジュールのリソース管理は、意外と手間がかかります。特に、リソースが共有されている場合には、リソース間の依存関係はDAGの形状になり、正しい順番で管理をしないと、クローズしたリソースにアクセスすることになり、例外(NullPointerException, IllegalStateExceptionなど)が発生する原因になります。

リソースの確保、解放は、FILO (First-In Last-Out)の順にすると安全です。上図の例のように、基本的には数字の順にobjectのリソースが割り当てられ、プログラムの終了時には逆順(8-> 7-> … -> 2 -> 1)で解放します。

オブジェクトの構築を手動で行う代わりに、フレームワークに任せることで、上図のようなDAGの形状をフレームワーク側が認識できるようになり、FILOオーダーでのリソース管理が自動的にできるようになります。

3: DIの存在自体を忘れる

Airframeは、フレームワークに依存した形でもコードをかけますが、コンストラクタインジェクションのみを使うことで、DI部分と実際のモジュールの実装を完全に分離することも可能です。

特に、Google Guice, ScaldiなどをScalaで利用して苦労していたエンジニアにはその効果が実感できるようです:

まとめ

Airframeでは、MessagePackを活用することにより各種データの操作を容易にしました。また、DIを通して、以下の3つに挙げるような、プログラマが実装時に考えるべきことを効率的に減らすことができます:

  • オブジェクトの構築方法
  • リソースのFILOオーダーによる確保、解放管理
  • DIフレームワークそのものの存在

Separation of concern(関心の分離)と言われることもありますが、具体例がないといまいち実感できない概念でもあるので、この記事では違った観点で紹介しました。

また、既に満席になってしまいましたが、2018年10月23日に、Airframeを使ったデザインパターンを紹介するためのAirframe Meetup #1を開催します:

最後に、AirframeのリポジトリにGitHubスターをつけていただけると、開発者が喜びますので、どうぞよろしくお願いいたします。

--

--

Taro L. Saito
Airframe

Ph.D., researcher and software engineer, pursuing database technologies for everyone http://xerial.org/leo