In previous posts, we discussed in depth the functional options pattern and listed the benefits of using this pattern over others such as the builder pattern when designing our APIs:
In this one, we are going to apply this pattern to another common use case in our projects, accessing a database. We are going to build a simple ORM to map our User type values from our database.
The go “database/sql” package let us to access to a database, and in most of the cases we can simply use this package directly. But at certain point, we are going to need to create our own package to encapsulate our database logic:
User Package
In the previous posts, we saw the problems with this type of API approach. The major problem with this API is that if we need to add another filter to the FindOne function, we are going to break the backward compatibility and force the consumer to change his code. Another problem we face is that we have to memorize or inspect the function code to figure the order of the parameters, and their default values. Also, is hard to read and test this code.
Builder pattern
One improvement to our API, is to use the builder pattern:
Builder
The consumer’s code results more comfortable to read now:
With the builder pattern we get rid of the parameters order problem, the default values problem, and makes the code easier to read and test.
In the other hand, we have to create a builder for every concrete type, and we still have to pass in some builder methods a parameter value.
Functional Options
Maybe we can improve this design with Functional Options. We are going to extract some methods and options from the user package to a db package to let us to reuse them in another domain’s data.
DB package with functional options
User package with functional options
As we can see, we made our API more friendly. Let’s review the benefits of using functional options in this particular case:
- Makes code easier to read and test it.
- Avoids breaking API breaks.
- Safe use of the API, avoids bad uses and values.
- Can be easily extended with our options implementation.
- Self documenting API.
- Highly configurable.
- Makes more consistent the default values behaviour.
You can view the functional options pattern in other user cases: