CleanSeed is a "Gold Standard" template for modern Java applications based on Spring Boot 3.2.12+ and Java 17+. It is designed following Clean Architecture (Hexagonal) principles to ensure strict decoupling, testability, and enterprise-grade maintainability.
The project is structured to enforce the Dependency Rule: dependencies point inwards toward the Domain. This ensures that the core business logic remains independent of frameworks, databases, or UI concerns.
cleanseed-domain: The Core. Contains Entities and Domain Collection interfaces. Zero external dependencies (except Lombok). It uses domain-driven naming (e.g.,SeedUsersinstead ofUserRepository) to treat persistence as an abstraction.cleanseed-application: The Orchestrator. Contains Use Cases and application orchestration. This layer is framework-agnostic (no Spring annotations) and coordinates data flow between the domain and outer adapters.cleanseed-infrastructure: The Technical Adapters. Contains implementation details: Spring Data JPA, database configurations (H2/PostgreSQL), and external service integrations.cleanseed-webapp: The Entry Point. The only module containing the@SpringBootApplicationclass. It hosts REST Controllers, Thymeleaf views, and DTOs to isolate the API contract from the internal domain.
This guide documents the exact process used to build this template, serving as a blueprint for future professional projects.
Instead of starting from the IDE, generate the "heart" of the application first to ensure a clean Spring Boot context.
- Use Spring Initializr: Maven, Java 17+, Boot 3.2.12+.
- Set Artifact to
cleanseed-webapp. - Add base dependencies:
Spring Web,Lombok,Validation. - Pro Tip: Unzip this into a root directory named
cleanseedto prepare for the multi-module shift.
To avoid IDE-generated mess, create the Parent pom.xml in the root folder by hand.
- Packaging: Set to
<packaging>pom</packaging>. - Centralized Management: Move the
spring-boot-starter-parentand global properties (like Java version) here. - Module Linking: Connect the webapp as the first module:
<modules> <module>cleanseed-webapp</module> </modules>
Create cleanseed-domain and cleanseed-application as sibling modules.
- Architectural Guardrail: By keeping the Domain in a separate Maven module, you physically prevent the business logic from importing Web or Persistence classes, strictly enforcing the Clean Architecture layers.
Implement the cleanseed-infrastructure module to bridge the Domain interfaces with Spring Data JPA.
- Security Audit: Run
mvn dependency:treeto verify that transitive dependencies (like Tomcat or SnakeYAML) are updated to non-vulnerable versions.
Before the first commit, configure the environment for cross-platform collaboration:
.gitignore: Exclude IDE files (.idea/), build artifacts (target/), and future frontend assets (node_modules/)..gitattributes: Define* text=autoand force specific line endings (eol=lffor Linux/Mac,eol=crlffor Windows scripts) to prevent "ghost" changes in Git.
- Transactional Boundary: Place
@Transactionalonly in dedicated facade/service classes incleanseed-webapp; keep application use cases framework-agnostic. - Pluralized Domain Collections: Use interfaces like
SeedUsersinstead ofSeedUserRepositoryin the domain. This promotes a "Collection" mental model rather than a "Database" one. - Records for DTOs: Utilize Java
recordfor Web responses to ensure immutability and concise code. - Safety: The project is patched against CVE-2024-57699 by ensuring Tomcat 10.1.33+ via Spring Boot 3.2.12.
- Tailwind CSS Integration: Using
frontend-maven-pluginfor a unified Java/Node build process. - CI/CD: GitHub Actions for automated testing.
- Security: Spring Security integration with a Clean Architecture approach.
mvn clean installmvn -Prelease clean verifyUse this profile in CI release pipelines to enforce non-SNAPSHOT dependency constraints.
Since this is a multi-module project, you must run the application from the root directory or specifically target the webapp module.
mvn spring-boot:run -pl cleanseed-webapp- Dove sta: il boundary transazionale è nel modulo
cleanseed-webapp, dentro facade/service dedicati (es.SeedOrderFacade). - Perché: il modulo
cleanseed-applicationdeve restare framework-agnostic per preservare testabilità, portabilità e regola delle dipendenze. - Vietato:
@Transactionalin controller e nel modulocleanseed-application.
- Boundary transazionale: i metodi applicativi (
cleanseed-application) restano framework-agnostic; la transazione Spring viene aperta nel layer di delivery (webapp). Trade-off: confine più "esterno" ma regola di dipendenza più pulita. - Dual persistence (Postgres + Mongo): non esiste transazione ACID unica cross-store. Usare Postgres per dati transazionali/consistenti, Mongo per read model/documenti denormalizzati. Trade-off: coerenza eventuale tra store.
- Soft delete first-class: tutte le aggregate persistenti devono avere strategia di soft delete coerente e query di default che escludono record/documenti cancellati.
- MVC security: session-based login + CSRF attivo + autorizzazione a ruolo su controller e viste Thymeleaf; nessuna dipendenza Spring Security nel dominio.
- Tailwind lean: preferire build semplice con CSS precompilato versionato; introdurre toolchain Node/Maven solo se serve personalizzazione avanzata.
- Domain puro: niente import Spring/JPA nel modulo
cleanseed-domain. - Use case in application: orchestration, policy e casi d'uso in
cleanseed-application. - Framework all'esterno: annotations Spring in
webapp/infrastructure, non nel dominio. - Controller sottili: binding/validazione/mapping DTO in webapp, business flow nei use case.
- Port naming esplicito: interfacce lato dominio/application con nomi ubiqui (es.
SeedOrders,SeedUsers). - Adapter separati per store: JPA adapter e Mongo adapter distinti, ciascuno su port chiari.
- Scelta Postgres vs Mongo: Postgres per invarianti e update consistenti; Mongo per document/read-heavy/flexible schema.
- No 2PC distribuito: tra Postgres e Mongo usare orchestrazione applicativa + retry/outbox dove necessario.
- Soft delete coerente:
deleted_at/deletedsu entrambe le persistenze, query default escludono cancellati, endpoint di restore/purge espliciti. - Guardrail automatici minimi: mantenere ArchUnit + Enforcer per prevenire leak architetturali.