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:
@@ -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]
|
||||
Reference in New Issue
Block a user