Add project-structure module: the same Boot 4.1 app laid out package-by-layer and package-by-feature, with the public surface counted and the javac boundary captured

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
This commit is contained in:
Claude
2026-10-21 16:43:45 +00:00
parent 008211f2fa
commit ba709737de
41 changed files with 1039 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
# 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 |