11. REST CRUD Implementation & Production Patterns

Hard vs Soft Delete, Pitfalls & Refactoring

Master hard vs soft deletion strategies, state transitions, query performance matrices, production pitfalls, engineering checklists, and refactoring God Controllers.

Hard vs Soft Delete, Pitfalls & Refactoring

Deleting records from a database is a critical architectural decision. Spring Boot CRUD applications support two deletion models: Hard Delete and Soft Delete.


1. Hard Delete vs Soft Delete Mechanics

Database Table: students

Before Deletion:
id | name          | email             | is_deleted
---+---------------+───────────────────+-----------
1  | Rahul Sharma  | [email protected] | false
2  | Amit Kumar    | [email protected]  | false

HARD DELETE (DELETE FROM students WHERE id = 1):
id | name          | email             | is_deleted
---+---------------+───────────────────+-----------
2  | Amit Kumar    | [email protected]  | false       <-- Row 1 is permanently destroyed!

SOFT DELETE (UPDATE students SET is_deleted = true WHERE id = 1):
id | name          | email             | is_deleted
---+---------------+───────────────────+-----------
1  | Rahul Sharma  | [email protected] | true        <-- Row retained for audit trail!
2  | Amit Kumar    | [email protected]  | false

Implementation Comparison

// Hard Delete (Permanent DB Row Removal)
public void hardDeleteStudent(Long id) {
    if (!studentRepository.existsById(id)) {
        throw new RuntimeException("Student not found: " + id);
    }
    studentRepository.deleteById(id);
}

// Soft Delete (State Flag Mutation)
public Student softDeleteStudent(Long id) {
    Student student = getStudentById(id);
    student.setDeleted(true); // Mutate flag
    return studentRepository.save(student); // Persist updated state
}

Deletion Strategy Trade-Off Matrix

MetricHard Delete (deleteById)Soft Delete (is_deleted = true)
Database RowPhysically removedRetained in storage
Data RecoverabilityIrrecoverable (unless restored from backup)Fully recoverable (is_deleted = false)
Storage GrowthMinimalContinuous table expansion
Query ComplexityStandard SQL queriesQueries must filter WHERE is_deleted = false
Compliance / AuditPoor for financial/legal recordsRequired for compliance and audit logging

2. Entity State Transition Model

                  POST /api/students (Create)


                       ┌──────────────┐
                       │ ACTIVE STATE │ (is_deleted = false)
                       └──────┬───────┘

            ┌─────────────────┴─────────────────┐
            │                                   │
   PUT /api/students/{id}              DELETE /api/students/{id} (Soft)
        (Update)                                │
            │                                   ▼
            ▼                            ┌──────────────┐
     ┌──────────────┐                    │ DELETED STATE│ (is_deleted = true)
     │ ACTIVE STATE │                    └──────┬───────┘
     └──────────────┘                           │
                                                │ Restore Operation

                                         ┌──────────────┐
                                         │ ACTIVE STATE │ (is_deleted = false)
                                         └──────────────┘

3. Database Operation Complexity Matrix

EndpointApplication IntentSQL ExecutedDB Performance CostIndex Requirement
POST /api/studentsInsert RecordINSERT INTO ...Low (1 Row Write)Primary Key Auto-Increment Index
GET /api/students/{id}Read SingleSELECT ... WHERE id = ?Very Low (O(1) / O(log N))Primary Key Index
GET /api/studentsRead AllSELECT ... WHERE is_deleted=falseHigh on Large TablesIndex on is_deleted column
PUT /api/students/{id}Update RecordSELECT then UPDATEMedium (1 Read + 1 Write)Primary Key Index
DELETE /api/students/{id}Soft DeleteUPDATE ... SET is_deleted=trueMedium (1 Read + 1 Write)Primary Key Index

[!WARNING] Performance Pitfall: Calling findAll() on a production table with millions of rows consumes massive memory, network bandwidth, and CPU cycles. Always implement Pagination using Pageable in production APIs!


4. Production Pitfalls & Engineering Checklist

5 Critical Pitfalls

  1. God Controller: Putting database queries and business rules inside @RestController methods.
  2. Unbounded findAll(): Returning entire database tables without pagination.
  3. Ignoring Missing IDs: Invoking repository.findById(id).get() without handling empty Optional results.
  4. Naming Inconsistencies: Mixing course and subject across DTOs, Entities, and SQL columns.
  5. Passive Database Assumptions: Adding JPA starters without providing spring.datasource.url, causing startup crashes.

Engineering Checklist

  • REST endpoints follow HTTP verb standards (POST, GET, PUT, DELETE).
  • Controller delegates all business logic to Service.
  • Repository inherits from JpaRepository<Student, Long>.
  • @Entity has @Id with @GeneratedValue(strategy = GenerationType.IDENTITY).
  • @PathVariable handles URL path IDs.
  • @RequestBody handles incoming JSON bodies.
  • Soft delete filters out isDeleted = true records in queries.
  • Connection parameters configured in application.properties.

❓ Interactive Self-Assessment

Knowledge Check

What is the main advantage of soft deletion over hard deletion in enterprise systems?

Knowledge Check

What risk occurs when calling repository.findById(id).get() directly?

Refactoring the God Controller

A legacy Spring Boot application has a StudentController that executes raw SQL via JDBC, validates inputs, applies business rules, and performs soft deletion logic directly inside the controller class. Refactor this architecture into clean, decoupled layers.

On this page