Abstracting the Database Using Hexagonal Architecture

In this lesson, we’ll apply what we’ve learned to our Wordz application and create a port suitable for fetching the words to present to a user.

Designing the repository interface

The first job in designing our port is to decide what it should be doing. For a database port, we need to think about the split between what we want our domain model to be responsible for and what we’ll push out to the database. The ports we use for a database are generally called repository interfaces.

The following three broad principles should guide us:

  • Think about what the domain model needs. Why do we need this data? What will it be used for?

  • Don’t simply echo an assumed database implementation, don’t think in terms of tables and foreign keys at this stage. That comes later when we decide how to implement the storage. Sometimes, database performance considerations mean we have to revisit the abstraction we create here. We would then trade off leaking some database implementation details here if it allowed the database to function better. We should defer such decisions as late as we can.

  • Consider when we should leverage the database engine more. Perhaps we intend to use complex stored procedures in the database engine. Reflect this split of behavior in the repository interface. It may suggest a higher-level abstraction in the repository interface.

Word fetching strategies

For our running example application, let’s consider the task of fetching a word at random for the user to guess. How should we divide the work between the domain and database? There are two broad options:

  • Let the database choose a word at random.

  • Let the domain model generate a random number and let the database supply a numbered word.

In general, letting the database do more work results in faster data handling; the database code is closer to the data and isn’t dragging it over a network connection into our domain model. But how do we persuade a database to choose something at random? We know that for relational databases, we can issue a query that will return results in no guaranteed order. That’s sort of random. But would it be random enough? Across all possible implementations? Seems unlikely.

Domain model decision and interaction

The alternative is to let the domain model code decide which word to pick by generating a random number. We can then issue a query to fetch the word associated with that number. This also suggests that each word has an associated number with it, something we can provide when we design the database schema later.

This approach implies we need the domain model to pick a random number from all the numbers associated with the words. That implies the domain model needs to know the full set of numbers to choose from. We can make another design decision here. The numbers used to identify a word will start at 1 and increase by 1 for each word. We can provide a method on our port that returns the upper bound of these numbers. Then, we’re all set to define that repository interface—with a test.

The test class starts with the package declaration and the library imports we need.

Get hands-on with 1400+ tech skills courses.