4.7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this is
A Spring Boot + Vaadin Flow project (Vaadin 25, Spring Boot 4, Java 25). UI is built entirely in server-side Java — no HTML/JS/TypeScript to write for views. Data layer is Spring Data JPA over H2 (file/in-memory, dev only).
Commands
./mvnw spring-boot:run # run the app (dev), http://localhost:8080
./mvnw test # run all tests
./mvnw test -Dtest=TaskServiceTest # single test class
./mvnw test -Dtest=TaskServiceTest#tasks_are_validated_before_they_are_stored # single test method
./mvnw package # production build -> target/*.jar
java -jar target/*.jar # run the built jar
No system Maven is required; always use the ./mvnw wrapper. There is no separate lint command configured.
Live hotswap (edit Java, see changes without restart) requires launching via the Vaadin IDE plugin (IntelliJ/VS Code/Eclipse) instead of spring-boot:run — not available from the CLI.
Port defaults to 8080 (override via server.port in src/main/resources/application.properties or the PORT env var).
Architecture
Feature-package structure: code is organized by feature/domain under com.example.<feature>, not by technical layer. Each feature package is self-contained: entity, repository, service, and a ui sub-package for its views. com.example.examplefeature is the template feature to copy/replace — its package-info.java has a TODO Remove this package once you have added real features.
Package visibility convention: types are package-private by default (e.g. TaskRepository, TaskService, TaskListView are not public). Only export a type outside its feature package when another package actually needs it. Follow this pattern for new features.
Null-safety: packages are annotated @NullMarked (JSpecify) in their package-info.java, so all types are non-null by default; use @Nullable explicitly where null is allowed (e.g. Task.dueDate). Apply @NullMarked to any new feature package.
Layout/routing: MainLayout (com.example.base.ui, annotated @Layout) is the shared AppLayout shell (drawer nav + header/footer) applied automatically to routed views. Side-nav entries are auto-discovered via @Menu on @Route view classes (see TaskListView) — no manual registration needed. ViewTitle is a shared composite for the per-view title bar (drawer toggle + heading).
Data access pattern: Slice<T> (via findAllBy(Pageable)) is preferred over Page<T> for grid data providers, since Slice avoids the extra COUNT query — see the comment in TaskRepository. Vaadin Grid is wired to Spring Data via VaadinSpringDataHelpers.toSpringPageRequest(query) in an in-memory/lazy DataProvider callback (see TaskListView).
Entities: JPA entities use @GeneratedValue(strategy = GenerationType.SEQUENCE), expose validation in setters (throwing IllegalArgumentException) rather than via bean-validation annotations, and implement equals/hashCode based on ID only (with a fixed hashCode, per the pattern in Task).
Schema management: spring.jpa.hibernate.ddl-auto=update is used for local dev convenience only. This is explicitly not appropriate for production — the app is meant to move to Flyway (or similar) for real schema migrations before shipping.
Frontend theming: Application sets the app-shell config (@Push, Aura theme via @StyleSheet(Aura.STYLESHEET), plus custom styles.css/view-title.css under src/main/resources/META-INF/resources/). No separate frontend build step is needed for typical UI work since Flow generates the client bundle; the vaadin-maven-plugin build-frontend goal runs as part of the Maven build.
Testing
Two distinct test styles are used:
- Service/integration tests (
TaskServiceTest):@SpringBootTest(webEnvironment = MOCK)+@Transactional(each test rolls back), asserting against the JPA layer directly. - UI/browserless tests (
TaskListViewTest): extendSpringBrowserlessTest(fromcom.vaadin:browserless-test-spring), which renders and interacts with actual Vaadin components server-side without a real browser. Usenavigate(ViewClass.class),test(component)for interactions/assertions, and$(ComponentClass.class)for component lookup queries. Views expose package-private fields (e.g.taskGrid,description,createBtn) specifically so tests in the same package can drive them directly.
MCP
.mcp.json configures a Vaadin docs MCP server (https://mcp.vaadin.com/docs) and a Playwright MCP server — both available for querying live Vaadin component/API docs and browser automation if needed.