sbt マルチプロジェクト構成を試してみた話


# sbt マルチプロジェクト構成を試してみた話
 
# この記事について
 
ふと、「そういえば sbt のマルチプロジェクト構成ってやったことないな・・・」と思ったのでやってみた話です。
 マルチプロジェクト構成で、サブプロジェクトから別のサブプロジェクトのコードを利用するところまでやってみました。
 
# 参考サイト
 
 - [sbt 公式 マルチプロジェクト](https://www.scala-sbt.org/1.x/docs/ja/Multi-Project.html)
 - [sbt 公式 ディレクトリ構造](https://www.scala-sbt.org/1.x/docs/ja/Directories.html)
 
# あらかじめやっておくこと
 
 JDK / sbt のインストールは行っておきます
 
# ディレクトリ構成
 
 下記のようなディレクトリ構成で進めようと思います
 
 ```md
 root
 - common // 共通処理
 - main // common を参照して実行する処理
 - build.sbt // ビルド定義
 ```
 
 common に共通処理を定義して、
 common の処理を main プロジェクトで利用してみます。
 

# プロジェクトをまとめる build.sbt の大枠を作ってみる
 
 プロジェクトのルートディレクトリにサブプロジェクトを含めたビルド定義ファイル `build.sbt` を用意します。
 
 今回は、 `common` / `main` という 2 つのサブプロジェクトを作りたいのでこの 2 つを定義してみます。
 `project in file("サブプロジェクトのディレクトリ名")` で Project 型の値を定義できるので、下記のようになります。
 
 ```scala:build.sbt
 // 複数プロジェクトで共通のセッティングをしたい場合、ThisBuild / で定義できる
 ThisBuild / version      := "0.1.0-SNAPSHOT"
 
 // commonSettingsという名前のセッティング Seq を作り、各プロジェクトの .settings に渡すことで共通の設定を行うこともできる
 lazy val commonSettings = Seq (
     scalaVersion := "2.13.3"
 )
 
 // common サブプロジェクトの定義
 lazy val common = (project in file("common"))
   .settings(
     commonSettings
   )
 
 // main サブプロジェクトの定義
 lazy val main = (project in file("main"))
   .settings(
       commonSettings
   )
 
 
 ```
 


# 別のプロジェクトのソースに依存する記述を build.sbt に追加する

依存関係をどう表現するのかについて [sbt公式 クラスパス依存性](https://www.scala-sbt.org/1.x/docs/ja/Multi-Project.html#クラスパス依存性) をみると、
 別のプロジェクトへの依存関係は `dependsOn` メソッドを使用することで実現できそうです。
 
ということで、`build.sbt` の main プロジェクトでこの `dependsOn` メソッドを使用し、common プロジェクトへの依存を定義してみます
 
 ```scala:build.sbt
 // main サブプロジェクトの定義
 lazy val main = (project in file("main"))
   .dependensOn(common) // common プロジェクトへの依存関係を定義
   .settings(
     commonSettings
   )
 ```
 
 これで main プロジェクト から common プロジェクトのソースを参照する準備ができました。
 ということで実際に参照してみます。
 
# 実際に main プロジェクトから common プロジェクトのソースを利用してみる
 
## common プロジェクトにクラスを追加
 
 適当なクラスを common プロジェクトに追加します。
 今回は `common/src/main/scala/common/model` 下に `Coffee.scala` という名前で作ってみました。
 
 ```scala:src/main/scala/common/model/Coffee.scala
 package common.model
 case class Coffee(name:String, price:Int)
 ```
 
 次にこのクラスを main プロジェクトの方から参照してみます。
 
## main プロジェクトから参照してみる
 
 適当なクラスを main プロジェクトに追加します。
 実行したいので `App` を継承した object にしておきます。
 
 別のプロジェクトの場合でも、package.class 名で import できそうなので、
 common プロジェクトの Coffee クラスも import してみます。
 
 ```scala:main/src/main/scala/main
 package main
 
 // 別のプロジェクトでも package.class名で import 
 import common.model.Coffee
 
 object MainApplication extends App {
     val coffeeList = List(Coffee("Blue Mountain", 650), Coffee("Kilimanjaro", 600))
     println(coffeeList)
 }
 
 ```
 
 (作って気づいたんですが、パスに main が入りすぎてて気持ち悪いですね。)
 
## 実行
 
 では、実際に実行して確認してみます。
 sbt でプロジェクトを指定して実行する場合は、`sbt プロジェクト名/コマンド` で実行できます。 
(sbt 起動しっぱなしなら sbt 内で `プロジェクト名/コマンド` で)
 
 今回は、main プロジェクトをただ実行したいので、下記コマンドで実行します。
 
 main プロジェクト実行コマンド : `sbt main/run` 
 
 実行結果をみてみると、、、
 
 ```scala
 [info] running main.MainApplication 
 List(Coffee(Blue Mountain,650), Coffee(Kilimanjaro,600))
 ```
 
 無事参照できてそうでした！！
 
# まとめ
 
 共通の処理を切り出したり、機能で分割するのに、マルチプロジェクトはとても良さそうでした。
 色々フレームワークやライブラリ触るときにプロジェクト作りまくってたのですが、それも整理できそうで嬉しいです。
 
(個人的に [Giter8](https://github.com/foundweekends/giter8) テンプレートを使うことが多かったのですが、
あまり sbt について深く知らずに使ってたなあ、としみじみ思ったので今回触ってみて色々気づきがあってよかったです。)