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")