Rich Domain Model with Hibernate
Last Updated :
04 Jul, 2024
Rich Domain Model in the context of software development and Hibernate is the model where the business logic is encapsulated directly within the domain objects, rather than being separated into the services or other layers. It can allow the domain model to enforce the business rules and ensure data consistency. This article will guide you through setting up the simple example project using Hibernate with Rich Domain Model.
In traditional applications, business logic can be spread across multiple layers, leading to a lack of cohesion and harder maintenance. The Rich Domain Model centralizes this logic within the domain entities. Making the code more intuitive and aligned with real-world business operations. This encapsulation aids in maintaining the business rule consistency and logic encapsulation.
For example, consider the simple banking application where the Account entities can handle operations like deposits and withdrawals internally rather than relying on external services to enforce the rules of the application.
Prerequisites:
- Basic knowledge of the Java and Spring Boot.
- Maven tool for dependency management.
- The Integrated Development Environment like IntelliJ IDEA.
Implementation of Rich Domain Model with Hibernate
Step 1: Create Spring Boot Project
Create a new Spring project using Spring Initializr and add the below dependencies into the project.
Dependencies:
- Spring Web
- Spring Data JPA
- MySQL Driver
- Lombok
- Spring DevTools
After creating the project, then the file structure will look like the below image.
Step 2: Application Properties
Open the application.properties file and add the configuration of the MySQL database and Hibernate.
spring.application.name=rich-domain-model
# MySQL Configuration
spring.datasource.url=jdbc:mysql://localhost:3306/example
spring.datasource.username=root
spring.datasource.password=
# JPA/Hibernate properties
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.show-sql=true
Step 3: Create the Account Entity
We will create the Account class with the methods to handle the deposits and withdrawals of the application.
Go to src > main > java > org.example.richdomainmodel > Account and put the below code.
Java
package org.example.richdomainmodel;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Data;
import java.math.BigDecimal;
@Entity
@Data
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private BigDecimal balance;
public Account() {
this.balance = BigDecimal.ZERO;
}
public void deposit(BigDecimal amount) {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Deposit amount must be positive");
}
this.balance = this.balance.add(amount);
}
public void withdraw(BigDecimal amount) {
if (amount.compareTo(this.balance) > 0) {
throw new IllegalStateException("Insufficient funds");
}
this.balance = this.balance.subtract(amount);
}
}
Step 4: Create the AccountRepository
Go to src > main > java > org.example.richdomainmodel > AccountRepository and put the below code.
Java
package org.example.richdomainmodel;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AccountRepository extends JpaRepository<Account, Long> {
}
Step 5: Create AccountController Class
Now, we will create the Simple RESTful endpoints to handle the HTTP requests of the application.
Go to src > main > java > org.example.richdomainmodel > AccountController and put the below code.
Java
package org.example.richdomainmodel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.Optional;
@RestController
@RequestMapping("/accounts")
public class AccountController {
@Autowired
private AccountRepository accountRepository;
// Create a new account
@PostMapping
public ResponseEntity<Account> createAccount(@RequestBody Account account) {
Account savedAccount = accountRepository.save(account);
return ResponseEntity.ok(savedAccount);
}
// Deposit money into an account
@PostMapping("/{id}/deposit")
public ResponseEntity<?> deposit(@PathVariable Long id, @RequestParam BigDecimal amount) {
Optional<Account> accountOptional = accountRepository.findById(id);
if (!accountOptional.isPresent()) {
return ResponseEntity.notFound().build();
}
Account account = accountOptional.get();
account.deposit(amount);
accountRepository.save(account);
return ResponseEntity.ok(account);
}
// Withdraw money from an account
@PostMapping("/{id}/withdraw")
public ResponseEntity<?> withdraw(@PathVariable Long id, @RequestParam BigDecimal amount) {
Optional<Account> accountOptional = accountRepository.findById(id);
if (!accountOptional.isPresent()) {
return ResponseEntity.notFound().build();
}
Account account = accountOptional.get();
try {
account.withdraw(amount);
accountRepository.save(account);
return ResponseEntity.ok(account);
} catch (IllegalStateException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
// Retrieve the balance of an account
@GetMapping("/{id}")
public ResponseEntity<?> getAccount(@PathVariable Long id) {
Optional<Account> account = accountRepository.findById(id);
return account
.map(a -> ResponseEntity.ok("Account ID: " + a.getId() + ", Balance: " + a.getBalance()))
.orElseGet(() -> ResponseEntity.notFound().build());
}
}
Step 6: Main Class
No changes are required in the main class.
Java
package org.example.richdomainmodel;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RichDomainModelApplication {
public static void main(String[] args) {
SpringApplication.run(RichDomainModelApplication.class, args);
}
}
pom.xml:
XML
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>rich-domain-model</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rich-domain-model</name>
<description>rich-domain-model</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Step 7: Run the application
Now, run the application and it will start at port 8080.
Step 8: Testing the Endpoints
1. Create Account Endpoint
POST http://localhost:8080/accounts
Output:
2. Retrieve Account balance through Id
GET http://localhost:8080/accounts/102
Output:
4. Deposit Endpoint
POST http://localhost:8080/accounts/102/deposit?amount=100
Output:
5. Withdraw Endpoint
POST http://localhost:8080/accounts/102/withdraw?amount=100
Output:
This example demonstrates the embedded critical business logic within the domain model itself and adhering to the principles of the Rich Domain Model with Hibernate in the Spring Boot application.