Final implementation chapter.
Read this chapter in Spanish: ES
This is where projects either become reliable, or quietly become fragile: persistence and tests.
Code references:
- src/tasks/ports/outputs/task_repository.rs
- src/tasks/adapters/persistence/in_memory_task_repository.rs
- src/tasks/adapters/persistence/json_file_task_repository.rs
Contract first, implementation second #
We defined TaskRepository before touching disk.
Why this instead of direct JSON calls from use cases #
If use cases know JSON details, they become infrastructure scripts.
With a repository port, use cases remain focused on business operations (save, find, list, delete) and adapters handle storage concerns.
Two adapters, two jobs #
InMemoryTaskRepository:
- fast,
- great for behavior tests,
- no filesystem overhead.
JsonFileTaskRepository:
- real persistence,
- serialization,
- I/O and parsing failure paths.
This split gives faster feedback and clearer fault isolation.
JSON persistence decisions #
- platform-aware data location via
directories; - create parent dirs when missing;
- missing file means empty initial state;
- invalid JSON returns explicit error.
Why not auto-heal corrupted JSON #
Because silent healing often means silent data loss.
Failing with context is less convenient in the moment, but much safer long term.
Operation semantics #
save: upsert byid.list:AllorByStatus.find_by_id:Option<Task>.delete:bool(deleted vs not found).
This keeps normal outcomes (like “not found” on delete) separate from real technical failures.
Test strategy #
Coverage includes:
- save/find by id,
- list all / list by status,
- delete existing / non-existing,
- persistence across repository instances,
- explicit error on invalid JSON.
JSON tests use tempdir, so user data remains untouched.
Commits that complement this chapter #
Explicit technical debt #
Still intentionally open:
RepoErroris broad (InternalError+ message),- no file locking for multi-process concurrency,
- no JSON schema versioning/migration layer.
Reasonable for current scope, important to tackle for heavier usage.
Closing #
The project started as a “simple todo CLI” and became a full Rust architecture exercise:
- domain invariants,
- use-case orchestration,
- adapter isolation,
- stable CLI contract,
- behavior-focused tests.
That is the real payoff: training scalable engineering decisions in a small, controlled project.