Java Coding Standards Core

This file is the default standard for Java backend changes. It defines the smallest set of rules that are non-negotiable plus high-value defaults. Use this first. Load deeper references only when needed.

1. Rule Tiers

  • REQUIRED: Must be followed unless the user explicitly asks for a justified exception.
  • DEFAULT: Preferred behavior; adapt to existing module patterns when needed.
  • PREFERRED: Good practice when it improves clarity and does not increase coupling.

2. Platform Baseline

  • REQUIRED: Target Java 21+ style (project baseline is Java 25).
  • REQUIRED: Use Jakarta APIs (jakarta.*), not javax.*.
  • DEFAULT: Prefer records for immutable DTO/value carriers.
  • DEFAULT: Prefer sealed types for closed polymorphism.
  • PREFERRED: Use pattern matching, text blocks, and var where readability improves.

3. Architecture and Dependency Direction

  • REQUIRED: Keep domain logic independent of transport/framework details.
  • REQUIRED: Domain code must not depend on Spring web, Mongo driver, or HTTP client specifics.
  • REQUIRED: Respect module boundaries; do not call another module's internal classes or repositories.
  • DEFAULT: For cross-module coordination, prefer events over direct synchronous calls.
  • DEFAULT: Use synchronous interfaces only for simple, stable dependencies.

4. Vertical Slice Structure

  • REQUIRED: Organize by business feature/slice, not horizontal technical folders.
  • REQUIRED: Keep most changes localized to one slice when implementing a feature.
  • DEFAULT: Use flat slice structure for simple features.
  • DEFAULT: Add api/domain/application/infrastructure subfolders only when feature complexity requires it.

5. Naming and Roles

  • REQUIRED: Use role suffixes consistently:
  • Controller, Handler, Repository, Gateway or Adapter, Event, Policy/Service, Factory, Assembler, Config.
  • REQUIRED: Use business-first names that communicate responsibility.
  • REQUIRED: Do not introduce ambiguous types such as *Manager, *Helper, *Utils, *Data for core domain behavior.

6. Domain Modeling

  • REQUIRED: Avoid primitive obsession for domain identifiers; use typed IDs/value objects.
  • REQUIRED: Enforce business invariants in domain/application layer, not only at HTTP boundary.
  • DEFAULT: Use immutable value objects with validation in construction paths.
  • DEFAULT: Keep aggregates behavior-focused; avoid anemic models when meaningful invariants exist.

7. Validation and Exceptions

  • REQUIRED: Validate request shape/format at API boundary.
  • REQUIRED: Validate business rules in handler/domain paths.
  • REQUIRED: Convert infrastructure exceptions to application/domain-relevant exceptions before crossing boundaries.
  • REQUIRED: Return stable error contracts from global exception handling.

8. Persistence and Tenant Safety

  • REQUIRED: Include tenant scoping in every tenant-owned query/filter.
  • REQUIRED: Keep repository contracts explicit; avoid hidden cross-tenant behavior.
  • DEFAULT: Keep transaction boundaries at handler/application level.
  • DEFAULT: Mark read paths as read-only transactional when supported by the stack.

9. Events and Integration

  • REQUIRED: Publish cross-module events only after successful state change.
  • DEFAULT: Prefer async event processing for side effects and decoupling.
  • DEFAULT: Include correlation metadata on cross-module or async events.
  • PREFERRED: Use transactional outbox for reliability-critical event delivery.

10. Testing Minimums

  • REQUIRED: Add or update tests for changed behavior.
  • REQUIRED: Cover business-critical paths with integration tests.
  • REQUIRED: Keep architecture checks that enforce boundaries and naming.
  • DEFAULT: Unit-test complex policies/value objects with deterministic edge cases.

11. Logging and Observability

  • REQUIRED: Use structured, parameterized logging (no string concatenation for log interpolation).
  • REQUIRED: Preserve request traceability across boundaries (trace/correlation + tenant/user context where available).
  • DEFAULT: Keep logs concise and high signal; avoid duplicative noise at multiple layers.

12. Legacy Adaptation Rule

  • REQUIRED: Do not force full rewrites of legacy slices when making scoped changes.
  • DEFAULT: Improve touched code toward these standards incrementally.
  • REQUIRED: If a required rule cannot be satisfied due to legacy constraints, note the constraint and the safest compliant compromise.

13. Quick Compliance Checklist

Use this before finalizing changes:

  • Does dependency direction still flow inward?
  • Are module boundaries and tenant boundaries preserved?
  • Are identifiers/domain values strongly typed where it matters?
  • Are naming suffixes and slice roles clear?
  • Are validation and exception mappings split correctly by layer?
  • Are events, transactions, and tests appropriate for risk?
  • Is the change incremental and pragmatic for existing code?

Read more