A Single Subtitle Field Touches Every Layer of a Backend
A field that takes five minutes to add on the frontend exposes every structural decision on the backend: schema defaults, validation boundaries, authorization rules, and cache-invalidation contracts. Missing any one of them produces a bug that looks like “the API works” but silently serves stale data or lets unauthorized callers through.
A frontend developer adding a subtitle input might see one form field and one API call. On the backend, that same field must be threaded through the SQL schema, the persistence entity, a validated request DTO, a response DTO, a facade contract, a controller endpoint, path-based security rules, and a cache eviction step. Each layer answers a different question: where the data lives, who can write it, what makes it valid, and how stale caches get cleared. The `fullstack-mall` codebase demonstrates this with a real `PATCH /api/admin/products/{id}/subtitle` endpoint, backed by `ProductSubtitleUpdateRequest`, `ProductFacade`, `ProductDbService`, and `ProductDetailCacheService`.
The walkthrough treats the field as a flow, not a feature. It starts at the database column — `subtitle VARCHAR(100) NOT NULL DEFAULT ''` — and moves outward through the entity, the DTOs, the facade’s trim-and-update logic, the controller’s thin HTTP handling, and the security config that gates `/api/admin/**` to the ADMIN role. A curl-driven verification section exercises the happy path, validation failures, 401/403 responses, and the 404 when a product doesn’t exist, then shows how to confirm cache eviction via Redis CLI.
Common mistakes — forgetting `@Valid`, placing a write endpoint on a public controller, skipping the response DTO, or rolling back a MySQL write because Redis deletion failed — are catalogued with their fixes. Appendices turn the lesson into reusable checklists for any field-level change, covering persistence decisions, input ownership, visibility, authorization, cache impact, and error contracts.
Frontend-to-backend transitions often underestimate field additions because the frontend cost is trivial; the real cost is maintaining consistency across six or more backend layers.
The project’s decision to let Redis eviction failures degrade gracefully (log and continue) rather than roll back a committed MySQL transaction reflects a mature fail-open posture for the caching tier.
Using `LambdaUpdateWrapper` with an explicit `affectedRows` check turns a simple UPDATE into a precise existence gate, which is the same pattern needed for inventory, order-state transitions, and payment captures.
Path-based authorization (`/api/admin/**` → ADMIN) removes the need for repetitive role checks in controller methods, but it also means a misplaced endpoint on a public controller becomes an instant security hole.
The checklist in the appendices generalizes the subtitle exercise into a decision tree — persist or not, user-filled or server-generated, public or admin-visible, cache-affecting or not — that applies to nearly every field-level story.