Full-stack monorepo with:
order-api/— Spring Boot 4.0.5 REST API (Java 25, PostgreSQL, JWT auth)order-ui/— React 19 SPA (JavaScript, Vite 8, Axios, Mantine)
Authentication is stateless JWT (10-minute expiry, no refresh tokens). The backend uses domain-grouped packaging; the frontend uses feature-grouped folders.
# Start the application (requires Postgres — see docker-compose.yml)
docker compose up -d # start Postgres 18.0
./mvnw clean spring-boot:run # run the API on :8080
# Build
./mvnw clean package # build JAR with tests
./mvnw clean package -DskipTests # build JAR without tests
# Run all tests
./mvnw test
# Run a single test class
./mvnw test -Dtest=OrderApiApplicationTests
# Run a single test method
./mvnw test -Dtest=OrderApiApplicationTests#contextLoads
# Run tests matching a pattern
./mvnw test -Dtest="*Controller*"Note:
OrderApiApplicationTestsuses@MockitoBeanto mock all infrastructure and runs without a live Postgres. New tests should use@WebMvcTestfor controllers,@DataJpaTestfor repositories, and@ExtendWith(MockitoExtension.class)for service unit tests.
# Install dependencies
npm install
# Start dev server on :3000
npm start
# Production build
npm run build
# Run all tests (CI mode — runs once and exits)
npm test
# Run a single test file by path pattern
npm test -- src/components/home/Login
# Run tests matching a name pattern
npm test -- -t "renders login"
# Lint source files
npm run lint
# Lint and auto-fix safe issues
npm run lint -- --fixESLint is configured via
eslint.config.js(flat config) using@eslint/jsrecommended as a base, pluseslint-plugin-reactandeslint-plugin-react-hooks. No Prettier config exists.
Note: Test files (
ComponentName.test.js/ComponentName.test.jsx) are co-located with every component. New tests should use@testing-library/react+@testing-library/user-event(both already installed).setupTests.jsregisters@testing-library/jest-dommatchers viaexpect.extend()and mocksmatchMediaandlocalStoragefor Mantine compatibility.
# Manual integration test script (requires both services running)
./order-api/test-endpoints.sh # zsh script; hits all endpoints with curl- Domain-grouped packages, not layered:
order/,user/,security/,rest/,rest/dto,config/,runner/ - Four REST controllers:
AuthController(/auth/**),OrderController(/api/orders/**),UserController(/api/users/**),PublicController(/public/**) - DTOs are Java records:
record LoginRequest(String username, String password) {} - Entities use Lombok:
@Data,@NoArgsConstructoron JPA entities — no manual getters/setters @RequiredArgsConstructoron all Spring beans instead of@Autowired@ResponseStatuson exceptions instead of@ControllerAdvice:All domain exceptions follow this pattern:@ResponseStatus(HttpStatus.NOT_FOUND) public class UserNotFoundException extends RuntimeException { ... }
UserNotFoundException(404 NOT_FOUND)DuplicatedUserInfoException(409 CONFLICT)UserDeletionNotAllowedException(400 BAD_REQUEST) — guards self-deletion and last-admin deletionOrderNotFoundException(404 NOT_FOUND)
- Service layer always has an interface +
ServiceImplimplementation (UserService/UserServiceImpl) - Optional used for nullable service lookups;
validateAndGet{Entity}By{Key}()methods throw on empty - Ordered repository queries: use Spring Data derived query methods for deterministic ordering — e.g.,
findAllByOrderByUsernameAsc()inUserRepository,findAllByOrderByCreatedAtDesc()inOrderRepository. Do not rely onfindAll()where order matters. - JWT errors: each exception type caught individually, logged with
@Slf4j, returnsOptional.empty() DatabaseInitializer(CommandLineRunnerinrunner/) seeds two default users on every startup:admin/admin(ADMINrole) anduser/user(USERrole). Becauseddl-auto: createdrops and recreates the schema on every boot, the user count is always 0 at startup, so seeding always runs.
- Function components with hooks only — no class components anywhere
- Routing: React Router v7 (
react-router-dom ^7) with<BrowserRouter>,<Routes>/<Route>, and<Navigate>; unmatched paths redirect to/ - UI library: Mantine v8 (
@mantine/core,@mantine/hooks) — requiresmatchMediamock in tests (provided bysetupTests.js) - Centralized API layer: all Axios calls live in
src/components/misc/OrderApi.js - Auth state managed via React Context in
src/components/context/AuthContext.jsx(useuseAuth()hook) - Error handling: always call
handleLogError(error)fromsrc/components/misc/Helpers.jsin catch blocks; setisErrorstate for UI feedback - Token expiry checked client-side in
AuthContext.userIsAuthenticated()and in the Axios interceptor before each request
Naming conventions:
- Classes:
PascalCase—OrderServiceImpl,TokenAuthenticationFilter - Methods/fields:
camelCase—validateAndGetUserByUsername,jwtExpirationMinutes - Constants:
UPPER_SNAKE_CASE—TOKEN_TYPE,BEARER_KEY_SECURITY_SCHEME - Packages: all lowercase —
com.ivanfranchin.orderapi.order - Exception classes:
{Domain}NotFoundException,Duplicated{Domain}Exception - Validate-or-throw:
validateAndGet{Entity}By{Key}naming pattern
Formatting:
- 4-space indentation
- Opening braces on the same line
- One blank line between methods
- Always include
@Override - Chained builder/stream calls: one method per line
Imports (order by convention — no enforced tool):
- Internal project imports (
com.ivanfranchin.*) - Third-party imports (Jakarta, JJWT, Lombok, Springdoc)
- Spring Framework (
org.springframework.*) - Java standard library (
java.*)
Types:
- Use
Long(boxed) for entity IDs;StringforOrder.id(UUID) - Return
List<T>, notCollection<T>orIterable<T> - Use Java records for all request/response DTOs
- Use
Optional<T>for nullable lookups in service methods
Bean Validation: Use @NotBlank, @Email, @Valid on controller parameters. No custom validators exist yet; add new ones following Jakarta Validation conventions.
Logging: Use @Slf4j (Lombok). Log JWT/auth errors with log.error(...). Do not use System.out.println.
Naming conventions:
- Component files and functions:
PascalCase—AdminPage.js,function AdminPage() {} - Non-component utility files:
PascalCase—Helpers.js,OrderApi.js,Constants.js - Hooks:
camelCasewithuseprefix —useAuth() - Event handlers:
handle{Action}—handleSubmit,handleDeleteUser,handleInputChange - State variables: descriptive
camelCase—isLoading,isError,orderDescription
Formatting:
- 2-space indentation
- Single quotes for all string literals (including JSX attributes)
- Arrow functions for handlers; named
functiondeclarations for components - No Prettier is configured — match the style of the file being edited
Imports (order by convention):
- React (
import React from 'react') - React ecosystem (
react-router-dom, context hooks) - Mantine / Tabler Icons components
- Local utilities (
OrderApi,Helpers) - CSS files last
No TypeScript, no PropTypes. The project is plain JavaScript. Do not introduce TypeScript without explicit instruction.
- Throw domain-specific
RuntimeExceptionsubclasses annotated with@ResponseStatus - Never use
@ControllerAdvice— the existing@ResponseStatusapproach is intentional - In JWT/filter code, catch each exception type individually and log; do not propagate as HTTP errors
- Wrap filter auth logic in
try/catch(Exception e)with logging
- Always call
handleLogError(error)(fromHelpers.js) inside everycatchblock - Set
isError(true)state for user-facing error display - Check
error.response.statusto differentiate 409 (conflict) vs 400 (validation) vs other errors - Never
console.logdirectly — usehandleLogError
- Use
@WebMvcTestfor controller tests withMockMvc(no full context) - Use
@DataJpaTestfor repository tests (embedded/test-container DB) — no repository tests exist yet; add them here when needed - Use
@SpringBootTestonly for true integration tests (requires Postgres) - Mock dependencies with
@MockitoBean(Spring Boot 4+ replacement for@MockBean) - Use
@WithMockUserfrom Spring Security Test for authenticated endpoint tests
- Place test files as
ComponentName.test.jsx(for components) orUtilityName.test.js(for non-JSX utilities) co-located with the component - Use
@testing-library/user-eventfor simulating user interactions - Use
@testing-library/jest-dommatchers (toBeInTheDocument,toHaveValue, etc.) - Mock
OrderApi.jscalls withvi.mock('../misc/OrderApi') - Run a single test:
npm test -- src/components/path/ComponentName - Shared test utilities live in
src/test-utils.jsx— userenderWithProviders(ui, { initialRoute })instead of barerender; it wraps the component inMantineProvider > MemoryRouter > AuthProvider. Fixtures available:makeAdminUser(),makeRegularUser(),makeExpiredUser(),makeToken(payload),seedLocalStorage(user). - Query strategy for form inputs: use
getByLabelTextfor inputs with alabel=prop (e.g. login/signup forms); usegetByPlaceholderTextfor search or action inputs that have only aplaceholder=prop and no label (e.g.OrderForm,OrderTable,UserTable). Never usegetByPlaceholderTexton an input that haslabel=.
- API base URL:
order-ui/src/Constants.js—config.url.API_BASE_URL; usesimport.meta.env.DEVto switch betweenhttp://localhost:8080(dev) and a production URL (prod) - JWT secret & expiry:
order-api/src/main/resources/application.yml—app.jwt.* - Postgres connection:
application.yml—spring.datasource.*; matches thedocker-compose.ymlservice - CORS:
order-api/.../security/CorsConfig.java— add new allowed origins toapp.cors.allowed-originsinapplication.yml;CorsConfig.javareads this value at startup via@Value - Swagger UI: available at
http://localhost:8080/swagger-ui.htmlwhen running locally - Security routes:
SecurityConfig.java— add new public/protected endpoint matchers here - Role constants:
SecurityConfig.ADMINandSecurityConfig.USER— reuse these strings; do not hardcode"ADMIN"or"USER"inline