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
@@ -0,0 +1,43 @@
# The same application, two layouts (src/main/java/com/ankurm/structure)
--- bylayer ---
bylayer/
ByLayerApp.java
controller/
BookController.java
dto/
BookRequest.java
BookResponse.java
exception/
ApiExceptionHandler.java
BookNotFoundException.java
model/
Book.java
repository/
BookRepository.java
service/
BookService.java
(9 source files)
--- byfeature ---
byfeature/
ByFeatureApp.java
book/
Book.java
BookCatalog.java
BookController.java
BookNotFoundException.java
BookRepository.java
BookRequest.java
BookResponse.java
BookService.java
loan/
Loan.java
LoanController.java
LoanRequest.java
LoanService.java
shared/
ApiException.java
ApiExceptionHandler.java
(15 source files)
@@ -0,0 +1,32 @@
# Top-level types per layout and whether each one is public
--- bylayer ---
public bylayer.ByLayerApp
public bylayer.controller.BookController
public bylayer.dto.BookRequest
public bylayer.dto.BookResponse
public bylayer.exception.ApiExceptionHandler
public bylayer.exception.BookNotFoundException
public bylayer.model.Book
public bylayer.repository.BookRepository
public bylayer.service.BookService
9 of 9 top-level types are public
--- byfeature ---
public byfeature.ByFeatureApp
package-private byfeature.book.Book
public byfeature.book.BookCatalog
package-private byfeature.book.BookController
package-private byfeature.book.BookNotFoundException
package-private byfeature.book.BookRepository
package-private byfeature.book.BookRequest
package-private byfeature.book.BookResponse
package-private byfeature.book.BookService
package-private byfeature.loan.Loan
package-private byfeature.loan.LoanController
package-private byfeature.loan.LoanRequest
package-private byfeature.loan.LoanService
public byfeature.shared.ApiException
package-private byfeature.shared.ApiExceptionHandler
3 of 15 top-level types are public
@@ -0,0 +1,8 @@
# Can code in one package reach another package's repository?
--- by layer: controller/Shortcut.java reaches into repository/ ---
compiled with 0 errors
--- by feature: loan/Shortcut.java reaches into book/ ---
error: com.ankurm.structure.byfeature.book.BookRepository is not public in com.ankurm.structure.byfeature.book; cannot be accessed from outside package
@@ -0,0 +1,35 @@
# The same requests against both layouts
--- bylayer ---
GET /books -> 200
[{"id":1,"title":"Effective Java","author":"Joshua Bloch","pages":416},{"id":2,"title":"Java Concurrency in Practice","author":"Brian Goetz","pages":384}]
GET /books/1 -> 200
{"id":1,"title":"Effective Java","author":"Joshua Bloch","pages":416}
GET /books/99 -> 404
{"detail":"Book 99 not found","instance":"/books/99","status":404,"title":"Not Found"}
POST /books -> 201
{"id":3,"title":"Clean Code","author":"Robert Martin","pages":464}
POST /books -> 400
{"timestamp":"2026-10-21T16:43:19.747Z","status":400,"error":"Bad Request","path":"/books"}
--- byfeature ---
GET /books -> 200
[{"id":1,"title":"Effective Java","author":"Joshua Bloch","pages":416},{"id":2,"title":"Java Concurrency in Practice","author":"Brian Goetz","pages":384}]
GET /books/1 -> 200
{"id":1,"title":"Effective Java","author":"Joshua Bloch","pages":416}
GET /books/99 -> 404
{"detail":"Book 99 not found","instance":"/books/99","status":404,"title":"Not Found"}
POST /books -> 201
{"id":3,"title":"Clean Code","author":"Robert Martin","pages":464}
POST /books -> 400
{"timestamp":"2026-10-21T16:43:20.250Z","status":400,"error":"Bad Request","path":"/books"}
POST /loans -> 201
{"id":1,"bookId":2,"bookTitle":"Java Concurrency in Practice","member":"Asha"}
POST /loans -> 404
{"detail":"Book 7 not found","instance":"/loans","status":404,"title":"Not Found"}
GET /loans -> 200
[{"id":1,"bookId":2,"bookTitle":"Java Concurrency in Practice","member":"Asha"}]
--- comparison ---
status codes and bodies identical for the five shared requests: true
@@ -0,0 +1,10 @@
# The controller is found only if it is in or below the @SpringBootApplication class's package
--- app class in scan.broken.app, controller in scan.broken.web (a sibling) ---
GET /hello -> 404
HelloController registered as a bean: false
--- app class in scan.fixed, controller in scan.fixed.web (below it) ---
GET /hello -> 200
HelloController registered as a bean: true
@@ -0,0 +1,14 @@
# A @SpringBootApplication class in the default package (no package statement)
source compiled:
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DefaultPackageApp {
}
--- what Spring Boot logged about the default package ---
WARNING ** : Your ApplicationContext is unlikely to start due to a @ComponentScan of the default package.
--- result ---
context FAILED to start: BeanDefinitionStoreException / root cause ConflictingBeanDefinitionException: Annotation-specified bean name 'bookController' for bean class [com.ankurm.structure.bylayer.controller.BookController] conflicts with existing, non-compatible bean definition of same name and class [com.ankurm.structure.byfeature.book.BookController]