10 Polymorphic storage
This chapter covers
- Using the sql package to interact with SQL databases.
- Idiomatic design patterns used in the sql and sql/driver packages.
- Integration testing database-dependent code effectively.
- Decoupling storage through consumer-defined interfaces.
- Enhancing modularity and composability with focused interfaces.
In earlier chapters, we built a link service for Bite. Now, another team needs persistent storage so links survive HTTP server restarts. We asked them to implement it and offered help with integration. This chapter shows how they added persistence to the project.
We'll start with the standard library's sql package and use SQLite because it's lightweight and needs no separate installation. However, the concepts apply equally well to other SQL databases, like PostgreSQL, because the sql package abstracts database interactions.
We'll then add a new Shortener service backed by SQLite and verify it using a test database. Lastly, we'll decouple HTTP handlers from this service using consumer-driven interfaces, which requires a different mindset from traditional object-oriented languages.
By the end, you'll know how to work with SQL databases using the sql package and effectively use interfaces to decouple implementations from the code that uses them.
10.1 Interacting with SQL databases
Go provides two packages to interact with a specific SQL (or row-oriented) database: