Scalaでマイクロサービス化を進めるために考えたこと

Adways Advent Calendar 16日目の記事です。

http://blog.engineer.adways.net/entry/advent_calendar/archive


こんにちは、古川です。現在アドテクチームに所属しています。

まさか、まさか一番最後になるとは思っていませんでした。。。あまりでかいことを書ける自信は持ちあわせてませんが頑張って書いていきたいとおもいますのでよろしくお願いします。

アドウェイズのアドテクチームでは、Perlで運用してきましたが、現在Scalaで既存サービスに対してマイクロサービスを進めてきています。

それを実践するにあたって、出てきた課題をどのように改善したかについて共有したいと思います。

マイクロサービスで取り組むにあたって出てきた課題

ビジネスロジックがWebフレームワークや具体的な実装に縛られていた

いままでの既存サービスはPerlのCatalystであり、MVCの考えで実装されていましたが、ビジネスロジックがコントローラやモデルに相当する箇所に書かれていたりしました。

「コントローラにビジネスロジックが書かれている」にはフレームワークのものと密結合しているため、このような問題がありました。

  • テストコードが書けない
  • つぎのことをやろうとしたときにビジネスロジックにも影響が及んでしまう
    • フレームワークのアップデートや切り替え
    • Webアプリの機能の切り捨て

一方、「モデルにビジネスロジックが混ざって書かれている」ケースについては、ビジネスロジックがモデルと結びついているため、これらの問題がありました。

  • 使われ方と結びついているため、そのモデルが複雑になる
  • モデルのテストコードも複雑になる
  • モデルに関するライブラリのアップデートなどをしようとしたとき、ビジネスロジックにも影響が及んでしまう

両方に共通している問題点は、ビジネスロジックがフレームワークや具体的な実装に縛られているような書き方にあり、これを防ぎたいと感じていました。

この問題については、つぎの手段で解決を試みました。

ヘキサゴナルアーキテクチャーの考えをプロジェクトに採用

「実践ドメイン駆動設計」の本を読んでいったところ、4章あたりにある「ヘキサゴナルアーキテクチャー」というのを見つけました。「ヘキサゴナルアーキテクチャー」はビジネスロジックが書かれるドメインやそれ以外の実装と明確に分離できるようなモデルだったので、まずはこの考えを取り入れることを検討しました。

他社さんの実装事例がないか確認

まずは、他社さんがすでにアーキテクチャーで実装している事例がないかさがしました。

するとセプテーニさんのところで「ヘキサゴナルアーキテクチャー」でプロジェクト構成している事例を見つけました。

Scalaで学ぶヘキサゴナルアーキテクチャ実践入門

最初は、ここに使っているプロジェクトをそのまま採用しようとしましたが、以下のような事情があったためしませんでした。

  • ほとんどの人がPerlやMVCフレームワークしかさわっていなかったため
  • 今までの方式と異なるこの手法を教えるのには時間がかかる

上記を参考にプロジェクトにヘキサゴナルアーキテクチャーをどう落とし込むか考えました。

WebフレームワークなどのUIやバッチについて

WebフレームワークなどのUIやバッチのエンドポイントと、ビジネスロジックであるドメインはプロジェクト単位でわけるようにしました。

具体的にはつぎのようにわけました。

  • web:Webフレームワークのコントローラなどを管理するところ
  • batch名:バッチの処理を管理するところ
  • core:ビジネスロジックなどのドメインを含む

そして、core以外のプロジェクトにビジネスロジックを埋めないようにしています。

このような方法をとることで、Webフレームワークなどとビジネスロジックを分離させることにしました。

MySQLへのアクセスする処理などについて

MySQLへのアクセスなどの処理については一旦coreのなかで管理することにしましたが、アプリケーション層・ドメイン層・インフラ層の3層に分けることで、ビジネスロジックと別々に管理するようにしました。

  • アプリケーション層:(使い所がまだ理解していないものの)Webフレームワークとビジネスロジックの間で調整する役割
  • ドメイン層:ビジネスロジック全体
  • インフラ層:DBアクセスなどの具体的な実装

DIパターンを採用を検討

ここまでの方法だけだと、書き方によってドメイン層がインフラ層に依存してしまいます。

そこで「DIパターン」を使って、ドメイン層がインフラ層に直接依存させないよう試みました。

従来のcake patternについて

まずは、一番最初に開発したScalaプロジェクトでは、こちらのページのCake Patternを採用していました。

Cake Pattern を理解する - daimatz.net -

この方法はぱっと見が複雑だったので、採用しませんでした。

Google Juiceを使った動的DIについて

つぎに、Google Juiceを使った動的DIの実装も検討しましたが、こちらについてはこのような問題があり、採用しませんでした。

  • Google Juiceの使い方について学ばなければいけない
  • 書き方も色々あってどれを使えばいいかわかりづらい
  • DIの仕組みがライブラリに依存してしまうので、ライブラリのアップデートで影響を受けてしまう
Minimal Cake Patternについて

さいごにドワンゴが考えた「Minimal Cake Pattern」という実装の仕方について検討しましたが、今回はこちらを採用しました。

Scalaにおける最適なDependency Injectionの方法を考察する 〜なぜドワンゴアカウントシステムの生産性は高いのか〜

採用した理由としては、

  • 従来のcake patternと比べて「Mixin」や「継承」のみしか使っていなく見た目が比較的シンプルだった
  • Google Juiceのように特定のライブラリに依存することがなかった

と、他の手法と比べてデメリットが克服されていたと感じたからです。

この手法をドメイン層からインフラ層を呼び出す箇所で利用しました。

Scalaのマイクロサービス化でやるべきことが非常に多かった

1つ目のマイクロサービスがある程度でき、この考えを他のチームに展開しようとしましたが、

プロジェクトを作るだけでも考えることが非常に多いことに気づきました。

たとえば、

  • 環境設定はどこに配置すればよいのか?
  • DBの接続はどのようにするのか?
  • ログはどのように吐かせるのが望ましいか?
  • プロジェクトの分離はどのようにすればいいのか? などなど

を考慮しなければいけませんでした。

ほかにも、sbtやlogbackの初期の設定でハマっていました。

ここに時間が割かれることを避けたかったため、つぎの手段をとりました。

最初のマイクロサービス化で作成したプロジェクトをベースにテンプレートを作成

そもそもうちの会社では、テンプレートのようなプロジェクトを持っていませんでしたので、今回それを持つことでこの手間の省力化を図ろうとしました。

そこで最初のマイクロサービス化で作成したプロジェクトをベースに、不要な箇所を取り除くことでテンプレートを作成することにしました。

というのも、このプロジェクトではある程度アーキテクチャーが固まっていたからです。

今回つくったテンプレートについては、簡単な実装例も混ぜたものを公開します。もしよければ、ご参考ください。

※ 名前等は若干変更を加えていますが、問題なく動作はできます。

github.com

最後に

既存サービスのマイクロサービス化にあたって上記のことを取り組んだことで、少なくともテストはだいぶ書きやすくなりました。

「改修しやすいか」という面についてはまだまだこれからですので、今回作成したテンプレートを他のプロジェクトに投入したり、運用させてみたりすることで検証してみたいと考えています。

また、今回のことを通してDDDについても部分的ですが学ぶことができました。

今後は、ユビキタス言語とかドメインやコンテキストマッピングとかも学び・実践していけば、よりよいサービスが作れると感じていますので、そちらにも力を入れていこうかと思います。