Card 07/ 09
An Interface That Mirrors the Database
The abstraction has a method per SQL query, so it still changes whenever MySQL does.
An interface shaped like the detail behind it is inversion in name only. If the methods mirror the database's tables or queries, the policy still changes whenever the database does. This card shows the difference between an interface shaped by the detail and one shaped by what the policy needs.
The expectation is that any interface between the service and MySQL counts as an inversion. So a team extracts one by copying the methods of their database class: select_user_by_id, select_user_emails_where, get_connection. The service now depends on an interface, and the class diagram looks correct.
Shaped by the detailMirrors the MySQL class
UserRepository with select_user_by_id(id) -> Row, get_connection() and begin_transaction(). The service reads columns from rows and manages transactions.
Shaped by the needWritten from the service's side
UserSource with email_for(user_id) -> str. The service asks for what it uses, in its own words, and knows nothing about rows.
The first interface leaks the database through its types and method names. Moving users to a web service that has no rows or transactions means changing the interface, and so changing the service, which was what inversion was meant to prevent. A fake for tests has to imitate rows and connections, so it is nearly as hard to write as the real thing.
The test for the second interface is to ask whether it would still make sense if the detail were replaced by something different. email_for makes sense whether users live in MySQL, a CSV file or a remote API. get_connection does not. Write the interface from the policy's side, in the policy's words, and let the implementation do the translating.
← → move through sections and cards