🗄️ B3 · Repository Layer: in-memory Map

B3 · Repository Layer: in-memory Map: The repository layer is the data's **archive clerk**: it does raw storage jobs — "store this bug", "fetch number 42", "delete" — but knows N

The repository layer is the data's **archive clerk**: it does raw storage jobs — "store this bug", "fetch number 42", "delete" — but knows NO business rules ("can this user delete?" is not its question). On this page, instead of a real database we use an in-memory `Map `; the goal is not to learn storage tech but to see the **separation of responsibilities across layers**. But why put storage in a separate layer — couldn't the controller write straight to the Map? Because if storage moves from a Map to a real database one day, only this layer should change while the business logic and controller above stay the same — the single-responsibility principle. In Java the equivalent is an `interface BugRepository` and its implementation; the caller knows only the `save`/`findById` signatures, not whether a Map or JPA sits behind it. In QA the repository is the key to **test isolation**: an in-memory repository can be reset before each test, so tests do not pollute each other's data — the "test fails because of a record left by the previous test" nightmare is solved here.

In-Memory Repository

**🐞 Defect Birth — if `findById` returns `null` instead of an empty `Optional`** **Code:** `public Bug findById(Long id) { return store.get(id); }` — returns `null` directly when the record is absent (no Optional). **What happens:** when the service calls `repository.findById(999).getSeverity()`, the missing record returns `null` and `.getSeverity()` throws a **NullPointerException**. Result: `GET /api/v1/bugs/999` returns **500 Internal Server Error** instead of 404. **Why sneaky:** "record not found" is actually a normal, expected case (404) — but returning `null` turns it into a server crash (500). A tester looking at the symptom says "server bug", while the root cause is a missing null-safety. **Where the tester catches it:** sending `GET /api/v1/bugs/999` with a nonexistent id and getting 500 while expecting 404. Returning `Optional` forces the upper layer to say "return 404 if absent" and prevents the crash.

🎬 404 or 500? Empty Optional vs null

A request wants a nonexistent record: GET /api/v1/bugs/999. The outcome depends on what the repository returns.

Path A — the repository returns null. When the upper layer calls `.getSeverity()`, a NullPointerException blows up → 500 Internal Server Error.

Path B — the repository returns an empty Optional. "No record" is no longer a crash but a handleable state.

An empty Optional forces the upper layer to say "return 404 if absent". The client gets the correct, expected response: 404 Not Found.

The lesson — "Not found" is a normal state (404), not a crash (500). Optional bakes this difference into the structure. The tester verifies by expecting 404 for a nonexistent id.

Why Layer Separation Brings Test Isolation

Repository only stores…

The in-memory Map has no business rules; it is just save/find/delete. This simplicity makes it ideal for tests.

Reset before each test…

Clearing the Map (store.clear()) wipes all data; each test starts from a fresh state.