Skip to content

Backend Structure

Paulo Gomes da Cruz Junior edited this page Apr 23, 2026 · 3 revisions

Code Structure Tree - Waste Plus Backends

Directory Layout

backend-applications/
|- nr-waste-plus-backend/                     # Spring Boot 4 BFF (PostgreSQL + external providers)
|  |- src/main/java/ca/bc/gov/nrs/hrs/
|  |  |- BackendApplication.java              # @SpringBootApplication entry point
|  |  |- configuration/                       # app config, security chain, metrics, CORS
|  |  |- controller/                          # /api/codes, /api/forest-clients, /api/search, /api/users
|  |  |- dto/
|  |  |  |- base/                             # shared enums and base records
|  |  |  |- client/                           # forest client API contracts
|  |  |  \- search/                           # reporting unit and search contracts
|  |  |- entity/
|  |  |  \- users/                            # user preference persistence entities (preferences, bookmarks)
|  |  |- exception/                           # ResponseStatusException hierarchy
|  |  |- provider/
|  |  |  |- forestclient/                     # forest client facade + clients
|  |  |  |- legacy/                           # legacy facade + clients
|  |  |  \- forwarders/                       # JWT and B3 propagation
|  |  |- repository/                          # Spring Data repositories
|  |  |- security/                            # customizers, role checks, auditor
|  |  |- service/                             # business orchestration services
|  |  \- util/                                # JWT, URI, and environment utilities
|  \- src/test/java/ca/bc/gov/nrs/hrs/
|     |- controller/                          # integration tests by endpoint group
|     |- provider/                            # integration/unit tests by upstream group
|     |- security/                            # authorization and role tests
|     |- util/                                # utility unit tests
|     \- extensions/                          # Testcontainers + @WithMockJwt support
|
\- nr-waste-plus-legacy/                      # Spring Boot 3 Oracle-backed service
   |- src/main/java/ca/bc/gov/nrs/hrs/
   |  |- LegacyApplication.java               # @SpringBootApplication entry point
   |  |- configuration/                       # app config, security chain, metrics, graceful shutdown
   |  |- controller/                          # /api/codes and /api/search
   |  |- dto/
   |  |  |- base/                             # identity, roles, code description
   |  |  \- search/                           # reporting unit and client search contracts
   |  |- entity/
   |  |  |- codes/                            # Oracle code-table entities
   |  |  |- reportingunit/                    # reporting unit entity
   |  |  \- search/                           # projection interfaces
   |  |- exception/                           # domain-specific HTTP exceptions
   |  |- mappers/                             # MapStruct mapper interfaces/implementations
   |  |- repository/
   |  |  |- codes/                            # code-table repositories
   |  |  \- *.java                            # native-query repositories + SQL constants
   |  |- security/                            # customizers, role checker, auditor
   |  |- service/
   |  |  |- codes/                            # district/sampling/status services
   |  |  \- search/                           # reporting and advanced search services
   |  \- util/                                # JWT and pagination helpers
   \- src/test/java/ca/bc/gov/nrs/hrs/
      |- controller/                          # MockMvc integration tests
      |- mappers/                             # mapping-specific unit tests
      |- security/                            # role and authorization tests
      |- util/                                # JWT and pagination unit tests
      \- extensions/                          # Oracle container + custom JWT test context

Folder Taxonomy Commentary

Shared layering

Both backends use the same core package layering:

  • controller/ for HTTP concerns and endpoint composition
  • service/ for business rules and orchestration
  • repository/ or provider/ for data access and integrations
  • dto/ for API contracts
  • security/ for filter-chain customizers and role logic
  • configuration/ for bean and framework wiring
  • exception/ for HTTP-aware domain failures
  • util/ for cross-cutting helper utilities

Layer intent by runtime

Layer Spring Boot 4 backend Spring Boot 3 legacy
Integration edge provider/ facade to Forest Client API and Legacy API mostly native SQL in repository/
Persistence compact PostgreSQL model for user preferences Oracle schema access with projections and native queries
Mapping mostly inline/manual mapping in services/providers explicit mappers/ package (MapStruct-centric)
Search domain external-orchestrated + enrichment core domain capability with query-heavy repository layer

Naming and Packaging Conventions

Package conventions

  • Keep root package as ca.bc.gov.nrs.hrs.
  • Use singular role folders unless domain grouping is explicit.
  • Group by technical layer first, then by domain where needed (service/codes, service/search).
  • Keep test package paths aligned with production packages.

Class naming conventions

Type Naming pattern Examples
Application entrypoint *Application BackendApplication, LegacyApplication
Configuration *Configuration SecurityConfiguration, HrsMetricConfiguration
Controller *Controller SearchController, CodesController
Service *Service ReportingUnitSearchService, UserService
Repository *Repository UserPreferenceRepository, OrgUnitRepository
Provider/Client *Provider, *Client LegacyApiProvider, ForestClientFetchClient
Mapper *Mapper DistrictMapper, ReportingUnitSearchMapper
Exception *Exception (or existing domain-aligned names) UserNotFoundException
Utility *Util JwtPrincipalUtil, PaginationUtil

DTO conventions

  • Keep DTOs in dto/<domain>/ packages (base, search, client).
  • Prefer immutable record DTOs for response payloads.
  • Keep enums under dto/base or feature-specific DTO subpackages.
  • Use clear suffixes for discoverability: *Dto, *ResultDto, *ParametersDto.

Structural Contract for New Backend Features

When introducing new backend features, follow this baseline shape:

ca/bc/gov/nrs/hrs/
|- controller/
|  \- <Feature>Controller.java
|- service/
|  \- <feature>/
|     \- <Feature>Service.java
|- dto/
|  \- <feature>/
|     |- <Feature>RequestDto.java
|     \- <Feature>ResponseDto.java
|- repository/ or provider/
|  \- <feature>/
|     \- <Feature>Repository.java or <Feature>Provider.java
|- exception/
|  \- <Feature>ErrorException.java
\- src/test/java/ca/bc/gov/nrs/hrs/
   |- controller/
   |- service/ or provider/
   \- <feature>/

Required expectations:

  1. Expose only controller-facing DTOs at API boundaries.
  2. Keep business logic in services, not controllers.
  3. Keep data-access and upstream-calling details out of services when a provider/repository boundary exists.
  4. Mirror package changes in tests.
  5. Preserve existing security and observability annotations/patterns.

Current Architecture Differences to Keep Intentional

  • provider/ exists in backend and is a first-class anti-corruption layer.
  • mappers/ exists in legacy and is central to projection-to-DTO translation.
  • repository/ in legacy contains heavier SQL query responsibilities than backend.
  • entity/ in backend is currently minimal; legacy has broader Oracle model coverage.

These differences are expected given runtime and data-source responsibilities.


Future-Proofing Recommendations

  1. Standardize mapper/ versus mappers/ naming when both services can align without disruptive refactor.
  2. Split oversized utility classes into focused units (claims, roles, principal identity, pagination).
  3. Keep SQL constants close to repositories but consider per-feature query modules as native SQL grows.
  4. Introduce @ControllerAdvice if exception mapping complexity increases across endpoints.
  5. Revisit entity package depth (entity/users vs entity/) as backend domain model expands.

Architectural Rules and Constraints

These rules apply to both backend applications and represent the architectural contract that all code contributions must respect.

Layering and API boundaries

  1. No business logic in controllers.
  2. No entity exposure outside the persistence layer.
  3. No @Transactional on controllers.
  4. Controllers must delegate to services; services delegate to repository or provider boundaries.
  5. No direct upstream HTTP calls from controllers.

Dependency injection and configuration

  1. No field injection with @Autowired.
  2. Use constructor injection via @RequiredArgsConstructor or @AllArgsConstructor.
  3. Use @Service, @Repository, or @Configuration over @Component when the role is clear.
  4. Do not use multiple @Value properties for a group of related configuration values — use @ConfigurationProperties instead.
  5. No circular dependencies.

Allowed exception:

  • Abstract MapStruct mapper classes where the framework requires field injection for abstract mapper wiring.

Security

  1. No @CrossOrigin on controllers.
  2. All new endpoints must be explicitly covered by route authorization rules.
  3. JWT role and claim extraction must remain within security/ or util/ package boundaries.
  4. Do not bypass configured security customizers with ad-hoc filters.

Persistence and JPA

  1. Avoid FetchType.EAGER unless explicitly justified in comments.
  2. Avoid CascadeType.ALL unless explicitly justified in comments.
  3. Prefer explicit mappedBy and ownership declarations on relationships.
  4. Keep native SQL within repository modules and documented inline.
  5. Multi-write service operations must define transaction boundaries.

Code quality

  1. No System.out.println in any application code.
  2. No wildcard imports.
  3. No static mutable state in utility classes.
  4. Classes exceeding 300 lines require a documented decomposition plan.

Scheduling and async

  1. No @Scheduled used outside a dedicated scheduler/ package.
  2. No @Async without explicit executor bean configuration.

Testing constraints

  1. No H2 for integration tests where Oracle or PostgreSQL behavior is required.
  2. No @SpringBootTest for pure unit tests.
  3. No mocked repositories inside integration tests that are meant to validate persistence or query behavior.
  4. Testcontainers should use a reusable static container per test suite.
  5. Use @DynamicPropertySource to wire container-provided datasource properties.
  6. Use descriptive test names; @DisplayName is recommended for integration tests.

Layer Review Checklist

Use this checklist when reviewing changes to code in any layer.

Controllers

  • No complex business logic blocks.
  • Response types are DTOs or view models, not entities.
  • No @Transactional annotation.
  • Dependencies point only toward services, injected via constructor.

Services

  • Orchestration and business rules remain in services.
  • No HTTP or controller concerns in service classes.
  • Multi-write paths define transaction boundaries.
  • Observability annotations are consistent with existing service pattern.

Providers (backend) and Repositories (legacy)

  • Provider facade pattern is used for all external API calls in the backend.
  • Native SQL stays within repository and query-constant modules in the legacy service.
  • No N+1 call or query patterns; flag for batching or caching where found.

Entities and Projections

  • JPA annotations are explicit and safe.
  • Optimistic locking applied where concurrent writes are possible.
  • Projection interfaces are placed and named consistently with existing patterns.

Security components

  • Route-level authorization rules cover any new endpoint.
  • Role checks and claim mapping are centralized, not duplicated in controllers or services.
  • No local bypasses of the configured security filter chain.

Tests

  • Test type matches the layer under test (unit or integration).
  • Testcontainers used for all DB-coupled integration tests.
  • Security changes include corresponding JWT-based integration test coverage.

Architecture Review Procedure

When reviewing a change for architectural compliance:

  1. Identify the layer or package each changed file belongs to.
  2. Check each file against the rules and checklist for its layer.
  3. Categorize any violations using the taxonomy below.
  4. Assign severity and document file, line, issue, and recommendation.

Violation taxonomy

Category Description
Layering and Boundaries Business logic, entity exposure, or HTTP concerns in the wrong layer
Spring Configuration and Dependency Injection Field injection, wrong stereotype, or @Value misuse
API Contract and DTO Boundaries Entity returned from controller, missing DTO packaging
Persistence and JPA/Hibernate Fetch strategy, cascade, native SQL placement
Security and Authorization Missing route coverage, bypassed filter, JWT logic in wrong layer
Observability and Reliability Missing tracing annotations, unhandled exceptions, silent failures
Testing Strategy and Testcontainers H2 use, missing Testcontainers, wrong test type
Naming and Packaging Conventions Incorrect suffix, wrong package, inconsistent naming
Build and Deployment Architecture Profile, plugin, or deployment manifest inconsistencies

Violation severity

Severity Meaning
CRITICAL Must be resolved before merge; breaks architectural contract, security, or correctness
WARNING Should be resolved soon; degrades maintainability or consistency
SUGGESTION Improvement opportunity with no immediate risk

Refactor Proposal Format

When proposing a refactor, provide:

  1. File path and line number
  2. Current behavior (what)
  3. Architectural problem (why)
  4. Proposed fix (how)
  5. Risk and migration impact

Waste Plus Wiki

Home

Architecture

Codes

Decisions

Knowledge Base

Development

Changes

Clone this wiki locally