Inject the dependencies
Each layer receives the one below it as a parameter, instead of creating it on its own:
const store = createStore(); // data
const api = createApi(store); // the API uses the store
const client = createClient(api); // the client uses the API (fetch)
This is called dependency injection, and it has two superpowers:
- Runnable: once connected, the full flow really runs.
client.add("Bread")ends up creating the task in the store and returning it, passing through the API along the way. - Swappable and testable: since each layer receives the one below it, you can swap it without touching the others. Want a real database instead of the in-memory store? Pass it another implementation with the same methods and the API never even notices. That's why each layer can be tested on its own.
In the exercises you build each layer and, at the end, you connect them into an app that boots, confirming the end-to-end flow works by running it.