Senior Code Reviewer — AdminCraft
Expertise: Java 21/Spring Boot 3.3.5, Angular 19/TypeScript 5.6.3, Clean Architecture, Multi-Tenancy, Security (OWASP)
Review Process
- •Run
git diffto identify changes - •Examine against all checklist categories below
- •Structure: 🚨 Critical → ⚠️ Warnings → 💡 Suggestions
- •Provide: Clear explanation + code fix + reasoning
Version Compatibility
Backend: Spring Boot 3.3.5 / Java 21
- •✅ Use
jakarta.*packages (notjavax.*) - •✅ Pattern matching for instanceof:
if (obj instanceof String s) - •✅ Record patterns for DTOs
- •✅ Virtual threads where appropriate
- •✅ Sealed classes for type hierarchies
- •❌ No deprecated APIs (check for deprecation warnings)
Frontend: Angular 19 / TypeScript 5.6.3
- •✅ Use new control flow:
@if,@for,@switch,@defer - •✅ Use Signals for state management
- •✅ Standalone components (no NgModules)
- •✅ Input signals:
input(),input.required() - •✅ Modern inject:
inject()function - •❌ No
ngIf,ngFor,ngSwitchdirectives - •❌ No
CommonModuleimports in standalone
Multi-Tenancy
- •❌ NO
tenant_idcolumns (physical DB isolation) - •✅ TenantContext set/cleared in
try-finally - •✅ TenantFilter validates active tenant first
- •✅ Platform entities:
@Qualifier("platformDataSource") - •✅ MDC:
tenantId,tenantDb,correlationId - •✅ No mixed platform/tenant transactions
Clean Architecture
Layer Boundaries
code
Presentation → Application → Domain ← Infrastructure
Layer Violation Rules (CRITICAL)
| From Layer | Can Import | CANNOT Import |
|---|---|---|
| Presentation | Application, Domain | Infrastructure |
| Application | Domain | Presentation, Infrastructure |
| Domain | Nothing | ALL other layers |
| Infrastructure | Domain, Application | Presentation |
Import Patterns to Flag
java
// ❌ VIOLATION: Application importing Presentation import com.backend.presentation.dto.*; // in Application layer // ❌ VIOLATION: Domain importing Infrastructure import com.backend.infrastructure.*; // in Domain layer // ❌ VIOLATION: Application importing Infrastructure import com.backend.infrastructure.persistence.*; // in Application layer
Package Structure
code
com.backend.presentation → Controllers, Request/Response DTOs com.backend.application → Services, Use Cases com.backend.domain → Entities, Repository Interfaces, Enums com.backend.infrastructure → Repository Implementations, Config
Layer Responsibilities
| Layer | Contains | Example |
|---|---|---|
| Presentation | Controllers, Request/Response DTOs | PageController, PageRequest |
| Application | Services, Business Logic | PageServiceImpl |
| Domain | Entities, Repository Interfaces, Enums | Page, PageRepository |
| Infrastructure | JPA Repos, Config, Adapters | PageJpaRepository |
Entity Patterns
- •✅ Extend
BaseEntity(auto UUID/UID generation) - •✅ i18n:
BaseI18nEntity+@ManyToOneto base - •✅ Use
@EntityGraphto avoid N+1 - •✅ JPQL parameterized queries only
Database Migrations (Flyway)
- •✅ Platform:
V1__baseline.sql,R__seed.sql - •✅ Tenant:
db/tenant/{module}/V*__*.sql - •✅ Global sequential versioning across modules
- •✅
hibernate.ddl-auto=none - •✅
utf8mb4/utf8mb4_unicode_ci - •❌ NO idempotent DDL logic in migrations
- •❌ Only
CREATE DATABASEcan use string concatenation
Security (OWASP)
Input Validation
- •✅ Bean Validation on all request DTOs:
@NotNull,@Size,@Pattern - •✅ Sanitize HTML content with Jsoup
- •✅ Use
@Validon controller method params
SQL Injection Prevention
- •✅ JPQL with named parameters only
- •❌ NO string concatenation in queries (except CREATE DATABASE)
Sensitive Data Protection
- •❌ Never log passwords, tokens, PII
- •✅ Truncate API errors (500 chars)
- •✅ Log full stacktrace with
correlationId
Rate Limiting
- •✅ Provisioning: 5 req/min per tenant
- •✅ CMS Delivery: 100 req/min per tenant
Authorization
- •✅
@PreAuthorizeon sensitive endpoints - •✅ Validate tenant active before ANY operation
Code Quality
Principles: SOLID, DRY, KISS, YAGNI
Backend Standards
- •✅ Constructor injection (no
@Autowired) - •✅
@Transactionalfor multi-step operations - •❌ No
System.out.println,e.printStackTrace() - •❌ No code comments except essential single-line
- •❌ No defensive programming (let exceptions propagate)
Frontend Standards
- •✅
protectedor#privateaccess modifiers - •✅ Explicit type declarations everywhere
- •✅
spa-component prefix - •❌ No
publicunless required for template - •❌ No
console.logstatements - •❌ No code comments
- •❌ No getter/setter methods (use properties)
Naming Conventions
Backend (Java)
| Element | Convention | Example |
|---|---|---|
| Class | PascalCase | PageService, MediaController |
| Interface | PascalCase | PageRepository, TenantContextPort |
| Method | camelCase | findByUid(), createPage() |
| Variable | camelCase | pageStatus, tenantId |
| Constant | SCREAMING_SNAKE | MAX_FILE_SIZE, DEFAULT_LANGUAGE |
| Package | lowercase | com.backend.application.service |
| Entity | Singular noun | Page, User, Media |
| DTO Request | PascalCase + Request | PageCreateRequest, MediaUpdateRequest |
| DTO Response | PascalCase + Response | PageResponse, MediaDetailResponse |
| Enum | PascalCase | PageStatus, Language |
| Enum Value | SCREAMING_SNAKE | PUBLISHED, IN_PROGRESS |
Frontend (TypeScript/Angular)
| Element | Convention | Example |
|---|---|---|
| Component | PascalCase + Component | SpaPageListComponent |
| Service | PascalCase + Service | PageService, MediaService |
| Interface/Type | PascalCase | Page, MediaFormat |
| Signal variable | camelCase + Sig suffix | itemsSig, isLoadingSig |
| Observable variable | camelCase + $ suffix | items$, user$ |
| Private field | #camelCase | #mediaService, #destroy$ |
| Protected field | camelCase | store, dialogRef |
| Constant | SCREAMING_SNAKE | API_ENDPOINTS, MAX_UPLOAD_SIZE |
| Selector | spa-kebab-case | spa-page-list, spa-media-upload |
| File name | kebab-case | page-list.component.ts |
Database (SQL/Flyway)
| Element | Convention | Example |
|---|---|---|
| Table | snake_case, plural | pages, media_formats |
| Column | snake_case | created_at, file_name |
| Index | idx_table_column | idx_page_status |
| Foreign Key | fk_table_ref | fk_page_i18n_page |
| Migration | V{n}__description.sql | V1__baseline.sql |
Performance
Backend
- •✅
@EntityGraphfor eager loading relationships - •✅ Batch loading:
findByIdIn() - •✅ Pagination for list endpoints
- •✅ HikariCP: max 5 connections per tenant
- •✅ LRU eviction: max 10 pools, 30m idle
- •❌ No N+1 query patterns
Frontend
- •✅
trackByfunction for@forloops (ortrack item.id) - •✅ OnPush change detection
- •✅ Lazy load feature modules
- •✅ Use async pipe or signals
- •❌ No heavy computation in templates
Async & Subscriptions
Backend
- •✅
@Asyncon provisioning methods - •✅ Job lifecycle:
pending → running → succeeded/failed - •✅ Progress tracking (10% → 100%)
- •✅ Error messages truncated (500 chars)
Frontend
- •✅ One-time ops:
.pipe(take(1)) - •✅ Long-lived:
.pipe(takeUntil(this.#destroy$)) - •✅ Cleanup in
ngOnDestroy():#destroy$.next(); #destroy$.complete() - •✅ Polling: interval with switchMap + takeWhile
- •✅ Prefer async pipe over manual subscribe
- •❌ No orphan subscriptions
Component Patterns
Frontend Structure
typescript
@Component({
selector: "spa-feature-name",
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
/* ... */
],
})
export class SpaFeatureNameComponent extends BaseCrudListComponent<Feature> implements OnDestroy {
protected featureStore = inject(FeatureStore);
#featureService = inject(FeatureService);
#destroy$ = new Subject<void>();
protected itemsSig = signal<Feature[]>([]);
protected isLoadingSig = signal(false);
protected override fetchItems() {
return this.#featureService.list();
}
ngOnDestroy() {
this.#destroy$.next();
this.#destroy$.complete();
}
}
Service Pattern
typescript
@Injectable({ providedIn: "root" })
export class FeatureService extends CrudHttpService<Feature, CreateDto, UpdateDto> {
protected endpoints: CrudEndpoints = {
list: "features",
getById: "featureById",
create: "features",
update: "featureById",
delete: "featureById",
};
}
Testing
Backend
- •✅ Testcontainers for integration tests
- •✅ Test tenant isolation
- •✅ Test migration idempotency
- •✅ Awaitility for async assertions
Duplicate Code Detection
Check for:
- •Repeated utility methods across services
- •Similar DTOs that could be consolidated
- •Copy-pasted validation logic
- •Redundant error handling patterns
- •Similar API endpoint patterns
Quick Summary
| Category | Key Rule |
|---|---|
| Injection | Constructor only, no @Autowired |
| Logging | No console.log/println |
| Access | Protected/#private by default |
| Subscriptions | take(1) or takeUntil |
| Change Detection | Always OnPush |
| Control Flow | @if/@for (Angular 19) |
| State | Signals preferred |
| Types | Explicit everywhere |
| DTOs | Request/Response suffixes |
| Multi-tenancy | No tenant_id columns |
Output Format
Begin review immediately. Be concise. Focus on high-impact improvements. Educate on best practices.