Scenario and definition
A Logistics Platform's backend team is splitting a monolith into a shipment-tracking service that must be onboarded quickly by new engineers, tested in isolation, and deployed independently. The team needs a folder structure that separates routing, business logic, data access, and configuration cleanly. In Node.js, this is a convention-driven layout enforced by team discipline and a chosen framework's recommendations (e.g., NestJS's module-based structure). In Spring Boot, this is a convention-over-configuration layout enforced by Maven/Gradle's standard directory structure and Spring's package-by-feature or package-by-layer norms.
Keywords
Code comparison
src/
modules/
shipments/
shipments.controller.ts
shipments.service.ts
shipments.repository.ts
shipments.module.ts
dto/
create-shipment.dto.ts
common/
filters/
guards/
interceptors/
main.ts
app.module.ts
package.json
tsconfig.jsonsrc/main/java/com/logistics/shipments/ controller/ShipmentController.java service/ShipmentService.java repository/ShipmentRepository.java dto/CreateShipmentRequest.java entity/Shipment.java config/ src/main/resources/ application.yml application-prod.yml pom.xml ShipmentsApplication.java
Code explanation
node
NestJS's module-based folder structure groups everything related to one feature (shipments.controller, .service, .repository, .module) into a single directory, mirroring how Spring Boot organizes by feature internally even though it's a Java convention rather than a NestJS-specific decorator. shipments.module.ts explicitly declares which providers and controllers belong to this feature and what it exports, giving Node.js the same explicit dependency-boundary declaration Spring achieves implicitly through package scanning. common/ houses cross-cutting concerns (guards, interceptors, filters) shared across modules, avoiding duplication.
springboot
Maven/Gradle enforces src/main/java and src/main/resources as non-negotiable root directories, which is why Spring Boot projects look structurally similar across companies — the build tool itself enforces this convention, unlike Node.js where the src/ layout is purely team-chosen. @SpringBootApplication on the main class triggers component scanning of the entire package tree beneath it by default, meaning ShipmentController, ShipmentService, and ShipmentRepository are auto-discovered and wired without explicit module declarations, the opposite of NestJS's explicit module registration.
Backend integration
node
shipments.module.ts wires the controller, service, and repository together via NestJS's dependency injection container, and this module is then imported into the root app.module.ts, which is what bootstraps the whole application in main.ts via NestFactory.create().
springboot
Spring's classpath component scanning, triggered from ShipmentsApplication's @SpringBootApplication annotation, automatically detects and registers @RestController, @Service, and @Repository-annotated classes anywhere under the base package without any manual module wiring step.
Interview questions
Why does NestJS require explicit module declarations (@Module with controllers/providers/exports) when Spring Boot achieves the same wiring through automatic component scanning?
NestJS modules give explicit, file-level control over what's visible to other modules — a service NOT exported from its module simply cannot be injected elsewhere, which enforces strong encapsulation boundaries between features by design. Spring's component scanning is more implicit and convenience-oriented; anything annotated and on the classpath is discoverable application-wide by default, which is faster to set up but requires more developer discipline to enforce the same encapsulation NestJS gives for free.
Why does Spring Boot enforce src/main/java and src/main/resources as fixed top-level directories while Node.js has no equivalent enforced convention?
Maven and Gradle, the build tools Spring Boot is built on, use a 'standard directory layout' that their build lifecycle phases are hardcoded to expect, so deviating from it requires explicit build-file reconfiguration; Node.js has no comparable build-tool-enforced directory contract — package.json's 'main' field and tooling like tsconfig's 'include' paths are fully configurable, leaving structure entirely up to team convention.