Describe how you'd structure a Spring Boot project (packages, layers) for a medium-sized application.
Quick Answer
A common, effective default is package-by-feature (or package-by-domain) rather than package-by-layer — grouping each business capability's controller, service, and repository together under one package (e.g., com.example.app.orders), rather than one giant controller package, one giant service package, and one giant repository package across the whole application. Within each feature package, a clear layered separation (controller -> service -> repository, each depending only downward) still applies; the main change is that the top-level organizing principle is the business domain, which scales much better as an application grows, and makes it far easier to eventually extract a feature into its own service if that ever becomes justified.
Detailed Answer
Two common top-level organizing principles, with a fairly clear winner for anything beyond a small application:
Package-by-layer (a common starting point, but scales poorly):
com.example.app
├── controller/
│ ├── OrderController.java
│ ├── CustomerController.java
│ └── ProductController.java
├── service/
│ ├── OrderService.java
│ ├── CustomerService.java
│ └── ProductService.java
└── repository/
├── OrderRepository.java
├── CustomerRepository.java
└── ProductRepository.java
The problem as an application grows: each package becomes a large, unrelated grab-bag of classes belonging to entirely different business features — finding "everything related to orders" means jumping across three separate, increasingly large packages, and it's easy for classes belonging to genuinely unrelated features to end up more coupled than they should be, simply because nothing about the package structure discourages it.
Package-by-feature (or package-by-domain) — group everything related to one business capability together:
com.example.app
├── orders/
│ ├── OrderController.java
│ ├── OrderService.java
│ ├── OrderRepository.java
│ └── Order.java
├── customers/
│ ├── CustomerController.java
│ ├── CustomerService.java
│ ├── CustomerRepository.java
│ └── Customer.java
└── products/
├── ProductController.java
├── ProductService.java
├── ProductRepository.java
└── Product.java
Why this scales better: everything related to one feature lives in one place, package-private visibility can be used to genuinely enforce that other features only interact with a feature through its intended public surface (rather than reaching directly into its repository, for instance), and — importantly — it's much easier to eventually extract a feature into its own separate microservice later if a real justification for doing so emerges (see the microservices-decision question), since its code is already cleanly bounded rather than smeared across three layer-wide packages.
Within each feature package, the layered separation still matters — a controller should still only talk to its own feature's service, which talks to its own repository, not the other way around; package-by-feature changes the top-level organizing axis, not the underlying layered-architecture discipline itself.
Practical guidance for an interview answer: default to package-by-feature for anything beyond a genuinely small application, note that layering discipline (controller → service → repository, each depending only downward) still applies within each feature package, and mention that this structure also happens to make a later "should this become its own microservice" decision much easier to execute cleanly if that need ever arises.