BOMプロジェクトとは

BOMプロジェクトについて調べたことまとめ。

参考:
- <http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#overview-maven-bom> => maybe DEAD
- <http://www.baeldung.com/spring-maven-bom>

Mavenを利用したJavaでの開発中にときどき `hoge-project-bom`のような名称で、pom.xmlだけを持つサブプロジェクトを目にすることがある。
これはBOMプロジェクトと呼ばれる特殊なプロジェクト形態の一種で、依存関係とそのバージョンを統一するために使われるもの。

ちなみにBOM(Bill Of Materials)とは製造業で用いる部品表を指す言葉らしい。

### What's it for? / なんのためのもの？

mavenプロジェクトが複数の子や孫プロジェクトを持つなど複雑な階層になっているとき、
親プロジェクト `hoge-parent`はモジュールAのバージョン1.0に依存しているのに 
子プロジェクト `hoge-child-a` はモジュールAのバージョン1.1に依存しており
他の子プロジェクト `hoge-child-b`はモジュールAのバージョン2.0に依存している、
など同一モジュールの依存バージョンがバラバラになっていると事故を起こしやすい。

上記のように単純ならまだわかりやすいが、推移的依存関係（Transitive Dependency）を持つ構造になると、
子の依存関係としてどのバージョンが解決されるかの管理が難しくなる。

その管理をしやすくするためのものがBOMプロジェクト。
BOMプロジェクトは基本的にpom.xmlのみを持ち、このプロジェクト全体で利用する各種モジュールのバージョンのみを記述しておく。
各種サブプロジェクトではモジュールのgroupIdとartifactIdのみを記述すれば、自動的にBOMで指定されたバージョンが解決される、という仕組みである。

### What to write / 何を書けばいいのか

BOMプロジェクトのpom.xmlに`<dependencyManagement>`セクションを含める。
このプロジェクト全体ではelasticsearchのバージョン2.3.5を使う事で統一したいとき、`<dependencyManagement>`の`<dependencies>`にelasticsearchの記述を含める。

``` {.xml}
<project ...>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>hoge-bom</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>hoge-bom</name>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.elasticsearch</groupId>
                <artifactId>elasticsearch</artifactId>
                <version>2.3.5-sp1</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
```

### How to use it / 使い方

BOMプロジェクトの利用方法は二通りある。
1) BOMを親プロジェクトにする方法と、2) BOMをインポートする方法である。

<!-- -->

1.  BOMを親プロジェクトにする方法

    以下のような構造のプロジェクトを想定する。

    ``` {.text}
    .
    └── hoge-bom
         ├── hoge-child-a
         └── hoge-child-b
    ```

    子プロジェクトのpom.xmlの`<parent>`要素にhoge-bomを指定する。
    これにより、`<dependencies>`要素ではelasticsearchのバージョン指定を省略することができる。

    ``` {.xml}
    <project ...>
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.example</groupId>
        <artifactId>hoge-child-a</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
        <name>hoge-child-a</name>
        <parent>
            <groupId>com.example</groupId>
            <artifactId>hoge-bom</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>

        <dependencies>
            <dependency>
                <groupId>org.elasticsearch</groupId>
                <artifactId>elasticsearch</artifactId>
            </dependency>
        <dependencies>
    </project>
    ```

2.  BOMをインポートする方法

    例えば以下のような構造のプロジェクトを想定する。

    ``` {.text}
    .
    ├── hoge-parent-A
    │   ├── hoge-child-a
    │   └── hoge-child-b
    ├── hoge-parent-B
    └── hoge-bom
    ```

    BOMを利用したいプロジェクトに下記の要領で`<dependencyManagement>`要素を追加し、scope=import,
    type=pomとしてhoge-bomを記述する。
    こうすることで、parentにした場合と同様に`<dependencies>`要素でモジュールのバージョン指定を省略できる。

    ``` {.xml}
    <project ...>
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.example</groupId>
        <artifactId>hoge-child-a</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
        <name>hoge-child-a</name>

        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>com.example</groupId>
                    <artifactId>hoge-bom</artifactId>
                    <version>0.0.1-SNAPSHOT</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>

        <dependencies>
            <dependency>
                <groupId>org.elasticsearch</groupId>
                <artifactId>elasticsearch</artifactId>
            </dependency>
        <dependencies>
    </project>
    ```

### \[おまけ\] About Transitive Dependency / 推移的依存関係について

あるパッケージへの依存が複数パス存在するとき、mavenではパスが短い方のバージョンとして解決される。
例えばAがBとEに依存しており、BとEはそれぞれ別のバージョンのDに行き着くとする。

``` {.example}
A -> B -> C -> D (v1.4)
A -> E -> D (v1.0)
```

このとき、Aのビルドに利用されるDのバージョンは、Dに行き着くパスが短い方のものとなる。
つまりこの場合は`A -> E -> D`と辿った先にある、バージョンv1.0のDである。