Skip to main content

Coding standards

Derived from the conventions the codebase already follows — match the surrounding code, and when in doubt copy the nearest existing example of the thing you're building.

Backend (Java / Spring Boot)

Layering — strict Controller → Service (interface + impl) → Repository → Entity. Entities never leave the service layer; DTOs cross the controller boundary. Packages are horizontal layers under com.motorph.payroll (controller, service, service.impl, repository, model, dto, …) — feature modules are name prefixes, not packages.

Naming — for a resource Foo:

ThingNameExample
REST controllerFooControllerEmployeeController.java
Service interface / implFooService / FooServiceImplPayrollServiceImpl.java
RepositoryFooRepositoryrepository/EmployeeRepository.java
Dynamic filtersFooSpecificationsrepository/specification/EmployeeSpecifications.java
Request DTOsFooCreateRequest, FooUpdateRequest, decision/status DTOs per actiondto/

No Helper/Utils2/FinalNew names. A class that doesn't fit the table gets a name that says what it does (DailyPayCalculator, MoneyRounder, ContributionRateCoverageValidator).

Conventions that are load-bearing:

  • @Transactional(readOnly = true) at service-impl class level; write methods override with @Transactional. Don't add method-level transactions to security-sensitive flows without reading why RefreshTokenService avoids them (backend/README.md).
  • Authorization is @PreAuthorize("hasAuthority(...)") with constants from PermissionConstants.java — never string literals in controllers, never role names.
  • Validation via Jakarta annotations on request DTOs + @Valid in the controller; business-rule violations throw IllegalArgumentException (→400) or ConflictException (→409) and are mapped centrally by GlobalControllerAdvice — don't try/catch in controllers.
  • DTO mapping is hand-written in the service impl by default; MapStruct is used only where it already exists (EmployeeMapper, TimesheetMapper). Follow whichever pattern the module you're touching uses.
  • Schema changes are Flyway migrations only — see backend/entities-and-migrations.md.
  • Time: use the injected Clock (ClockConfig), timezone Asia/Manila for anything user-facing or scheduled.

Frontend (TypeScript / React)

File pairing — a domain foo gets a thin axios module api/foo.ts (typed request/response, no logic) and a React Query wrapper hooks/api/useFoo.ts (query keys + mutations that invalidate them). Pages go in pages/<area>/, domain components in components/<area>/, and anything reusable across domains in ui/. Don't import from pages/ into ui/.

Naming — components PascalCase.tsx, hooks useX.ts, stores xStore.ts, constants in constants/. Route paths come from constants/routes.ts; permission strings from constants/permissions.ts (mirror of the backend constants — keep role-permissions.ts in sync with RBAC migrations).

Load-bearing conventions:

  • Server state lives in React Query; the Redux store is for auth only; Zustand for the portal/UI islands. Don't add Redux slices or context providers for data — see frontend/state.md.
  • Data tables use the enterprise grid subsystem (useEnterpriseGrid + ServerDataGrid), configured, not forked (frontend/ag-grid.md). The scaffolding commands in .claude/commands/ (ag-grid-server-table, add-import-export) generate a compliant page.
  • Forms: React Hook Form + Zod schema colocated with the form component, submitted through a React Query mutation, server errors surfaced via getApiErrorMessage → toast (frontend/forms.md).
  • Chakra v3 composition rules (Portal/Positioner for menus & tooltips, stable component trees) — frontend/styling.md.
  • TypeScript is strict (noUnusedLocals, noUnusedParameters, verbatimModuleSyntax); npm run build type-checks the whole app — keep it clean, don't any your way past errors.
  • Lint gate: npm run lint (ESLint incl. react-hooks rules) must pass.

Documentation

  • Living docs go under docs/ in the right subtree and get a row in docs/README.md. Journals/planning go to docs/archive/.
  • Code references in docs are relative links to the actual file so they stay clickable and verifiable.

Tests

  • Backend: JUnit 5 tests beside the layer they test (backend/src/test/java/...), named <ClassUnderTest>Test.
  • E2E: Playwright specs by module directory in e2e/, using the shared helpers (e2e/helpers/serverGrid.ts) and demo personas — read e2e/README.md first; most suites run serial.