author-pic

Ferry S

An ISTJ, Type 5, Engineer, Gamer, and Thriller-Movies-Lover
Maven: Mengatur Dependency Pakai BOM
Sat. Oct 15th, 2022 07:50 PM5 mins read
Maven: Mengatur Dependency Pakai BOM
Source: flickr@Jernej Furman - bill of materials on orange background

Ketika kita mengembangkan aplikasi microservice, biasanya kita akan memecah module tersebut menjadi beberapa submodule. Terkadang kita agak kesulitan memaintain dependency dari masing-masing module. Salah satunya ketika melakukan sentralisasi dependency yang digunakan agar seragam. Misalkan kita ingin menambahkan dependency Jackson-Databind. Kita ingin semua module menggunakan versi yang sama, yaitu versi 2.13.4.1. Bisa saja kita deklarasikan satu-persatu ke tiap submodule, tapi ketika ingin upgrade tentu repot juga dong kalau harus ubah versinya satu-persatu di tiap module. Jika menggunakan Maven, umumnya kita membuat parent module untuk sentralisasi module. Sebagai contoh kita akan bikin module dengan “parent-module” sebagai parent, dan “order” & “invoce” sebagai submodule.

Parent-module pom.xml

<project>
	<groupId>org.example</groupId>
	<artifactId>parent-module</artifactId>
	<packaging>pom</packaging>
	<version>1.0-SNAPSHOT</version>

	<modules>
		<module>order</module>
		<module>invoice</module>
	</modules>
</project>

Order pom.xml

<project>
	<parent>
		<artifactId>parent-module</artifactId>
		<groupId>org.example</groupId>
		<version>1.0-SNAPSHOT</version>
	</parent>

	<artifactId>order</artifactId>
</project>

Invoice pom.xml

<project>
	<parent>
		<artifactId>parent-module</artifactId>
		<groupId>org.example</groupId>
		<version>1.0-SNAPSHOT</version>
	</parent>

	<artifactId>invoice</artifactId>
</project>

Ada beberapa cara untuk memaintain dependency.

Kita bisa deklarasi dependency tersebut di parent seperti berikut:

Parent-module pom.xml

<dependencies>
	<dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
		<artifactId>jackson-databind</artifactId>
		<version>2.13.4.1</version>
	</dependency>
</dependencies>

Dengan begitu otomatis semua dependency pada submodule ikut mewarisi Jackson-Databind dari parent. Ga perlu deklarasi ulang dependency tersebut di submodule. Nanti tiap upgrade, cukup upgrade versi di parent module. Kita ga perlu ubah di tiap submodule. Pemakaiannya sangat simple. Tapi kelemahannya adalah, misalkan submodule “order” ga butuh Jackson, dan hanya dibutuhkan submodule “invoice” doang. Kalau dependency dideklarasi di parent, itu artinya submodule “order” juga kecipratan Jackson.

Selain deklarasi dependency, kita juga bisa membuat tag properties yang berisi versi dari Jackson.

Parent-module pom.xml

<properties>
	<jackson.version>2.13.4.1</jackson.version>
</properties>

Invoice pom.xml

<dependencies>
	<dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
		<artifactId>jackson-databind</artifactId>
		<version>${jackson.version}</version>
	</dependency>
</dependencies>

Jika menggunakan properties tag, kita bisa melakukan sentralisasi version menggunakan tag yang dibuat pada parent. Berbeda dengan parent declaration, disini kita harus deklarasi ulang dependency Jackson pada submodule yang ingin menggunakan Jackson, dalam hal ini submodule “invoice”. Submodule “order” yang tidak menggunakan Jackson tidak akan kecipratan dependency Jackson. Permasalahan pada cara sebelumnya teratasi😎. Pada bagian version kita menggunakan variable yang dideklarasikan pada properties. Tiap upgrade tinggal ubah value pada properties tag di parent pom. Itu hanya berlaku jika module tersebut memiliki “parent-module” sebagai parent. Kalau bukan submodule dari “parent-module” ga bisa.

Selain dengan properties tag, cara lainnya adalah menggunakan BOM (Bill of Materials), yaitu menggunakan tag dependencyManagement pada parent.

Parent-module pom.xml

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>${jackson.version}</version>
		</dependency>
	</dependencies>
</dependencyManagement>

Invoice pom.xml

<dependencies>
	<dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
		<artifactId>jackson-databind</artifactId>
	</dependency>
</dependencies>

Pada parent, kita deklarasikan Jackson lewat dependency management. Pada submodule “invoice” cukup deklarasi ulang group & artifact Jackson saja. Version-nya otomatis mengikuti versi pada dependency management di module parent. Disini lebih simple, karena ga perlu deklarasi version. Ketika upgrade cukup ubah version pada dependency management di module parent. Dependency yang dideklarasi pada dependency management tidak akan diwariskan submodule secara otomatis. Jadi disini Jackson hanya ada submodule “invoice” saja. Permasalahan pada cara pertama juga teratasi mengguanakan cara ini😎.

Misalkan ada kasus tertentu yang mengharuskan kita menambahkan dependency dengan versi yang berbeda dengan BOM pada sebuah submodule. Contohnya, pada submodule “order” kita ingin ada Jackson tapi dengan versi 2.13.4.2. Maka kita tinggal deklarasi dependency Jackson seperti biasa termasuk dengan menuliskan version 2.13.4.2 pada submodule “order” agar versionnya ga ngikut version yang ada pada BOM.

Order pom.xml

<dependencies>
	<dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
		<artifactId>jackson-databind</artifactId>
		<version>2.13.4.2</version>
	</dependency>
</dependencies>

Tag dependency management dengan dependency berbeda. Tag dependency artinya kita akan memanage sekaligus menambahkan dependency tersebut ke module. Sedangkan tag dependency management hanya untuk memanage dependency saja tanpa menambahkan dependency tersebut ke module. Untuk menambahkan dependency tersebut ke module kita tetap harus menambahkan tag dependency.

Keunggulan menggunakan BOM adalah kita ga hanya memanage versi dependency, tapi juga hal lain seperti exclusions, scope, systemPath, dll. Jadi nanti pada submodule cukup deklarasi group & artifact saja pada tag dependency tanpa harus menambahkan tag lainnya yang sudah dideklarasi pada Dependeny Management. Selain itu, kita juga bisa mengimport module yang berisi BOM ke module lain yang bukan submodule dari “parent-module”. Contohnya seperti berikut:

Another module pom.xml

<project>
	<groupId>org.example</groupId>
	<artifactId>another</artifactId>
	<version>1.0-SNAPSHOT</version>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.example</groupId>
				<artifactId>parent-module</artifactId>
				<version>1.0-SNAPSHOT</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
</project>

Module di atas adalah module baru yang bukan submodule dari “parent-module”. Tapi kita tetap bisa menggunakan BOM dari “parent-module” dengan cara mengimportnya dengan cara deklarasi dependency management dan masukkan module “parent-module” dengan scope import.

Demikian 3 cara memaintain dependency pada Maven. Cara yang paling direkomendasikan adalah menggunakan BOM karena lebih simple cukup deklarasi group & artifact pada submodule, lebih fleksibel tanpa harus jadi submodule dari parent, ga perlu takut transitive ke semua submodule seperti cara pertama, dan juga bisa maintain beberapa hal seperti version, exclusions, scope, systemPath, dll.