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: