Spring Boot
Capstone – Article 12: Reporting & Aggregations (Expense Tracker)
By Utility Zone · 2026-01-27T18:33:19.792471
1. Introduction
CRUD APIs are not enough for real applications. Reports and summaries are what users actually consume.
In this article, we will:
- Add aggregation queries
- Build monthly expense summaries
- Build category-wise reports
- Create dashboard-ready APIs
This turns your backend into a decision-support system.
2. Types of Reports We Will Build
For the logged-in user:
- Total expenses for a month
- Category-wise totals for a month
- Overall spending summary
All reports must:
✔ Respect JWT user context
✔ Be efficient (DB-level aggregation)
3. Why Aggregation at Database Level
❌ Aggregating in Java:
- Fetches too much data
- Slow for large datasets
✔ Aggregating in DB:
- Faster
- Scalable
- Industry best practice
We will use JPQL queries.
4. Creating Projection DTOs
4.1 CategorySummary DTO
package com.example.expensetracker.dto;
import java.math.BigDecimal;
public class CategorySummary {
private String category;
private BigDecimal totalAmount;
public CategorySummary(String category, BigDecimal totalAmount) {
this.category = category;
this.totalAmount = totalAmount;
}
// getters
}
4.2 MonthlySummary DTO
package com.example.expensetracker.dto;
import java.math.BigDecimal;
public class MonthlySummary {
private BigDecimal totalAmount;
public MonthlySummary(BigDecimal totalAmount) {
this.totalAmount = totalAmount;
}
// getters
}
5. Adding Aggregation Queries
Update ExpenseRepository:
@Query("""
SELECT new com.example.expensetracker.dto.CategorySummary(
e.category,
SUM(e.amount)
)
FROM Expense e
WHERE e.user.id = :userId
AND MONTH(e.expenseDate) = :month
AND YEAR(e.expenseDate) = :year
GROUP BY e.category
""")
List<CategorySummary> getCategorySummary(
@Param("userId") Long userId,
@Param("month") int month,
@Param("year") int year);
5.1 Monthly Total Query
@Query("""
SELECT new com.example.expensetracker.dto.MonthlySummary(
SUM(e.amount)
)
FROM Expense e
WHERE e.user.id = :userId
AND MONTH(e.expenseDate) = :month
AND YEAR(e.expenseDate) = :year
""")
MonthlySummary getMonthlyTotal(
@Param("userId") Long userId,
@Param("month") int month,
@Param("year") int year);
6. Updating ExpenseService
public List<CategorySummary> getCategorySummary(int month, int year) {
User user = getLoggedInUser();
return expenseRepository.getCategorySummary(user.getId(), month, year);
}
public MonthlySummary getMonthlySummary(int month, int year) {
User user = getLoggedInUser();
return expenseRepository.getMonthlyTotal(user.getId(), month, year);
}
private User getLoggedInUser() {
String email = SecurityUtil.getCurrentUserEmail();
return userRepository.findByEmail(email)
.orElseThrow(() -> new UserNotFoundException("User not found"));
}
7. Creating Report Controller
@RestController
@RequestMapping("/reports")
public class ReportController {
private final ExpenseService expenseService;
public ReportController(ExpenseService expenseService) {
this.expenseService = expenseService;
}
@GetMapping("/monthly")
public MonthlySummary getMonthlySummary(
@RequestParam int month,
@RequestParam int year) {
return expenseService.getMonthlySummary(month, year);
}
@GetMapping("/category")
public List<CategorySummary> getCategorySummary(
@RequestParam int month,
@RequestParam int year) {
return expenseService.getCategorySummary(month, year);
}
}
8. Testing Report APIs
Examples:
GET /reports/monthly?month=1&year=2026
GET /reports/category?month=1&year=2026
Responses are dashboard-ready.
9. Sample Response
Category summary:
[
{ "category": "FOOD", "totalAmount": 5200.50 },
{ "category": "TRAVEL", "totalAmount": 2100.00 }
]
Monthly summary:
{
"totalAmount": 7300.50
}
10. Why This Is Interview Gold
This article shows:
✔ Aggregation skills
✔ JPQL knowledge
✔ Performance awareness
✔ Real reporting logic
Very few candidates go this far.
11. Common Mistakes
❌ Aggregating in Java streams
❌ Forgetting user constraint
❌ Returning raw Object[]
❌ Poor API naming
12. Git Commit (Important)
git add .
git commit -m "Add expense reporting and aggregation APIs"
13. What You Should Have Now
At this point:
- Reporting APIs exist
- Monthly & category summaries work
- Backend is analytics-ready
14. What’s Next?
➡ Capstone – Article 13: Performance, Caching & Optimization
- Cache reports
- Improve performance
- Production tuning
Type Next when you’re ready 🚀