Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01HhW8LD8r3EigoBaCGrQZx5 Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01HhW8LD8r3EigoBaCGrQZx5
52 lines
2.2 KiB
Markdown
52 lines
2.2 KiB
Markdown
# project-structure
|
|
|
|
Companion project for the article **[Spring Boot Project Structure: Package-by-Layer vs Package-by-Feature](https://ankurm.com/spring-boot-project-structure/)** on **[ankurm.com](https://ankurm.com)**.
|
|
|
|
One small library application (books, plus loans in the feature layout) written twice, with the same
|
|
HTTP API, so the only difference between the two is where the files live. Every console block quoted in
|
|
the article came out of `output/`, written by the test suite, so a claim that stops being true turns the
|
|
build red.
|
|
|
|
There is deliberately **no `docs/` folder**: the deeper material lives in collapsible sections inside the
|
|
article itself.
|
|
|
|
## Versions
|
|
|
|
| | |
|
|
|---|---|
|
|
| Spring Boot | 4.1.1 |
|
|
| Spring Framework | 7.0.9 |
|
|
| JDK | 21 (OpenJDK 21.0.12) |
|
|
| Maven | 3.9 |
|
|
|
|
The module was compiled and run on JDK 21 only; it has not been run on JDK 25.
|
|
|
|
## Quickstart
|
|
|
|
```bash
|
|
mvn test # runs everything and rewrites output/01-04
|
|
./scripts/run-all.sh # same, then lists output/
|
|
mvn spring-boot:run # starts the recommended (by-feature) layout on :8080
|
|
```
|
|
|
|
## Source layout
|
|
|
|
| Package | What it is |
|
|
|---|---|
|
|
| `bylayer/` | package-by-layer: `controller/`, `service/`, `repository/`, `model/`, `dto/`, `exception/` |
|
|
| `byfeature/book/` | the book feature: everything package-private except `BookCatalog`, the feature's public API |
|
|
| `byfeature/loan/` | the loan feature: uses books only through `BookCatalog` |
|
|
| `byfeature/shared/` | cross-cutting: `ApiException` and the one `@RestControllerAdvice` |
|
|
|
|
Both applications have their own `@SpringBootApplication` (`ByLayerApp`, `ByFeatureApp`), and each scans
|
|
only its own package, so the two never see each other's beans.
|
|
|
|
## Captured output
|
|
|
|
| File | What it shows |
|
|
|---|---|
|
|
| `01-package-trees.txt` | the real source tree of both layouts (9 files vs 15 files) |
|
|
| `02-public-surface.txt` | every top-level type and whether it is `public`: 9 of 9 by layer, 3 of 15 by feature |
|
|
| `03-compiler-boundary.txt` | the same one-line shortcut (`new BookRepository()` from another package): compiles by layer, rejected by `javac` by feature |
|
|
| `04-same-api-both-layouts.txt` | five identical requests against both layouts, plus the loan feature's three requests |
|