More often than not, I reach for the repository pattern when I'm writing a client app for a server-based application.
The problem it solves: Keeping data consistent across an app without requiring a network request each time that data is displayed.
Example of problem: Say you have an app with two screens: a list of articles screen and the single article screen. Both screens have a π button that shows how many people have πβd the article.
If an article has 40 π on the article list, it should have 40 π in the single article view.
If a user presses π in the single article view, the number should increment to 41 π. When returning to the article list, the article should also have 41 π.
Parts that go in the pattern:
The user interface:
When implementing a piece of UI that needs data from the API, let that piece of UI fetch it from a repository.
ThumbButton { articleID = "abc123" repository = /* ... */ render() { return "π <<repository.thumbCountFor(articleID)>>" } }
To keep the displayed data up to date, have the UI listen for changes from the repository and update itself if anything changes.
ThumbButton { start() { repository.listenForChange(handleRepositoryUpdated) } end() { repository.stopListeningForChange(handleRepositoryUpdated) } handleRepositoryUpdated() { this.rerender() // The data will be refetched from the repository on the next // render. } }
The network data source:
ThumbNetworkDataSource { thumbCount(articleID) { return network.thumbCount(articleID) } addThumb(articleID) { network.addThumb(articleID) } }
The local data source:
ThumbLocalDataSource { thumbCounts = {:} // an empty mapping }
ThumbLocalDataSource { thumbCount(articleID) { return network.thumbCount(articleID) } setThumbCount(articleID, thumbCount) { thumbCounts[articleID] = thumbCount this.notifyListeners() } }
The repository:
At this point, the repository's responsibilities are much clearer: