So! We've separated the
Task object from its persistence. An important practical benefit of that separation is that other parts of the code base--for example, a part that accepts and processes requests to add and delete tasks--can do their work independently of the details of that persistence.
The repository is still not independent enough, however. It's great that the (e.g.) request processor isn't concerned with serialization and deserialization, but it is still communicating with the repository through the functions that repository exposes. And, in general, the functions a particular repository exposes will reflect their specific ways of persisting things.
Different parts of your code should communicate through well-designed interfaces. This is often considered an advanced software topic, but it's never too early to start thinking about it. You can read more about this on my software design resource page. It's OK if you don't understand everything. Nobody does.
For now, this means that--yes, even now, while we're in the early stages of a prototype--we need to (i) figure out what the relevant interface is; (ii) define it in code; (iii) make it explicit that TaskRepository conforms to it; and (iv) revise the rest of our code to interact with that interface, rather than particular instantiations of it.
We'll do (i)-(iv) now and leave (v) for later.
Note that we'll use "TaskRepository" as the name of the interface from now on, renaming (what is currently)
There's not much new code here, but extracting interfaces is an essential technique, to be studied carefully. (If Michael Feathers had offered no intellectual legacy other than emphasizing the importance of extracting interfaces, he would have had an admirable career. Happily, he's contributed much more than that.)