Modern software projects often consist of dozens (or hundreds) of dependencies. Managing versions, transitive dependencies, consistency, and build logic manually quickly becomes chaotic. Apache Maven addresses this complexity using two foundational concepts:
- POM (Project Object Model)
- BOM (Bill of Materials)
Though related, they serve distinct purposes in dependency and build management.
What is POM (Project Object Model)?
A POM is the core configuration file of a Maven project. It defines:
- Project metadata
- Dependencies
- Plugins
- Build lifecycle configurations
- Inheritance and modules
- Repository definitions
Every Maven project must have one POM file, named: pom.xml
┌──────────────────────────┐
│ pom.xml │
│ (Project Object Model) │
└──────────┬───────────────┘
│
│ Defines
▼
┌──────────────────────────┐
│ Dependencies │
│ Plugins │
│ Build Lifecycle │
│ Packaging (jar/war) │
│ Repositories │
│ Project metadata │
└──────────────────────────┘
Core Idea Behind POM
The POM acts as the single source of truth for how a project is built, tested, packaged, and deployed.
Basic Structure & Syntax of a POM
Minimal POM Example
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo-app</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
</project>
Key Elements of a POM
a. Dependencies
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.5</version>
</dependency>
</dependencies>
b. Plugins
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
c. Inheritance (Parent POM)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version>
</parent>
What is BOM (Bill of Materials)?
A BOM is a special type of POM whose sole purpose is to centralize dependency versions.
A BOM does not build an application.
It only manages versions.
Technically, a BOM is still a POM, but with:
<packaging>pom</packaging>
Core Idea Behind BOM
Imagine a project with:
- 20 Spring dependencies
- 15 Kafka-related dependencies
- 10 internal libraries
If versions are declared individually:
- Inconsistencies arise
- Version conflicts appear
- Upgrading becomes risky
BOM solves this by declaring versions exactly once.
BOM Syntax & Structure
BOM Definition Example
┌──────────────────────────────┐
│ BOM (POM) │
│ packaging = pom │
│ │
│ Dependency Versions Only │
│ │
│ spring-core → 6.1.5 │
│ spring-web → 6.1.5 │
│ spring-context → 6.1.5 │
│ jackson-* → 2.15.3 │
└──────────────────────────────┘
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>example-bom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.1.5</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
How to Use a BOM in a POM
Application POM
│
├── dependencyManagement
│ └── import example-bom
│
├── dependencies
│ ├── spring-core
│ ├── spring-web
│ └── jackson-databind
│
▼
Maven Resolves Versions From BOM
Importing a BOM
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>example-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Using Dependencies Without Versions
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
Versions are automatically resolved from the BOM.
Differences Between POM and BOM
| Aspect | POM | BOM |
| Purpose | Build & manage a project | Manage dependency versions |
| Packaging | jar / war / pom | pom only |
| Contains dependencies | ✅ Yes | ✅ Yes (in dependencyManagement) |
| Builds an artifact | ✅ Yes | ❌ No |
| Declares plugin config | ✅ Yes | ❌ Typically no |
| Used via inheritance | ✅ Yes | ✅ Yes |
| Used via import | ❌ No | ✅ Yes |
┌────────────────────┐
│ Parent POM │
│────────────────────│
│ Plugins │
│ Build Config │
│ Java Version │
│ Repositories │
└─────────┬──────────┘
│ inherits
▼
┌─────────────────────────────────────────────────────┐
│ Application POM │
│─────────────────────────────────────────────────────│
│ Uses Plugins & Build from Parent │
│ │
│ dependencyManagement │
│ └── import → BOM │
│ │
│ dependencies │
│ ├── spring-context │
│ ├── spring-web │
│ └── jackson-databind │
└─────────────────────────────────────────────────────┘
▲
│ imports
┌─────────┴──────────┐
│ BOM │
│────────────────────│
│ Dependency Versions│
│ Only │
└────────────────────┘
| Component | Controls |
| Parent POM | How you build |
| BOM | Which versions you use |
| App POM | What you actually need |
Typical Use Cases
POM Use Cases
- Define application dependencies
- Configure build plugins
- Package applications
- Create parent POMs
- Multi-module project management
BOM Use Cases
- Enforce consistent dependency versions
- Share version catalogs across teams
- Avoid “dependency hell”
- Simplify upgrades
- Manage large microservice ecosystems
Handling Multi-module project with BOM
Company BOM
(Versions for All)
▲
│
┌────────────┴───────────┐
│ Parent POM │
│ (Plugins & Build Rules)│
└────────────┬───────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌───────────┐ ┌───────────┐ ┌───────────┐
│ service-a │ │ service-b │ │ service-c │
│ pom.xml │ │ pom.xml │ │ pom.xml │
│ deps w/o │ │ deps w/o │ │ deps w/o │
│ versions │ │ versions │ │ versions │
└───────────┘ └───────────┘ └───────────┘
What we achieved:
- Upgrade Spring → 1 BOM change
- Enforce security versions → 1 BOM change
- Zero duplication across services
Real‑World Examples
Spring Boot BOM
Spring Boot Starter Parent
│
└── imports
└── spring-boot-dependencies (BOM)
│
├── spring-framework
├── tomcat
├── jackson
├── logback
└── hundreds more
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Spring Boot internally manages hundreds of compatible versions.
Best Practices
✅ Use a BOM when:
- You manage multiple services
- You want version consistency
- You maintain internal libraries
✅ Use a Parent POM for:
- Plugin configuration
- Build logic
- Common repositories
✅ Combine both:
- Parent POM → build logic
- Imported BOM → dependency versions