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:
| Thing | Name | Example |
|---|---|---|
| REST controller | FooController | EmployeeController.java |
| Service interface / impl | FooService / FooServiceImpl | PayrollServiceImpl.java |
| Repository | FooRepository | repository/EmployeeRepository.java |
| Dynamic filters | FooSpecifications | repository/specification/EmployeeSpecifications.java |
| Request DTOs | FooCreateRequest, FooUpdateRequest, decision/status DTOs per action | dto/ |
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 whyRefreshTokenServiceavoids 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 +
@Validin the controller; business-rule violations throwIllegalArgumentException(→400) orConflictException(→409) and are mapped centrally byGlobalControllerAdvice— 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 buildtype-checks the whole app — keep it clean, don'tanyyour 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 todocs/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.