Careeroza
Package Structure
Package by Layer vs Package by Feature
# Package by layer (common in tutorials) # Package by feature (scales better)
com.shopease com.shopease
├── controller/ ├── ShopEaseApplication.java
│ ├── ProductController.java ├── product/
│ └── OrderController.java │ ├── ProductController.java
├── service/ │ ├── ProductService.java
│ ├── ProductService.java │ ├── ProductRepository.java
│ └── OrderService.java │ ├── Product.java
├── repository/ │ └── dto/ (CreateProductRequest, ProductResponse)
├── entity/ ├── order/
└── dto/ │ └── ...
└── common/
├── error/ (GlobalExceptionHandler)
└── config/ (CorsConfig, OpenApiConfig)Tip: Package by feature keeps everything about "product" together. You can make classes package-private, so other features must go through ProductService.
The Main Class Goes on Top
@SpringBootApplication scans its own package and all sub-packages for components. Keep it in the root package (com.shopease); a controller in com.other would never be found.
Exercise: A teammate put ShopEaseApplication in com.shopease.app and now GET /api/products returns 404. Why, and what are the two fixes?
Show answer
// ProductController lives in com.shopease.product, which is NOT under com.shopease.app,
// so component scanning never finds it.
// Fix 1 (best): move ShopEaseApplication to the root package com.shopease
// Fix 2: @SpringBootApplication(scanBasePackages = "com.shopease")