rss twitter gitlab github linkedin linkedin instagram
Microservices in Go: Repository Pattern
Apr 04, 2021

Continuing with the theme of using Domain Driven Design for building Microservices in Go, today let’s talk about the Repository Pattern.



Disclaimer: This post includes Amazon affiliate links. If you click on one of them and you make a purchase I’ll earn a commission. Please notice your final price is not affected at all by using those links.


What is the Repository Pattern?

According to Martin Fowler in Patterns of Enterprise Application Architecture (1st Edition) a Repository:

Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.

He continues (emphasis mine):

Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers.

In Domain Driven Design the Repository Pattern relates another DDD term called Aggregate, Martin Fowler defines it as:

… a cluster of domain objects that can be treated as a single unit.

He continues (emphasis mine):

Aggregates are the basic element of transfer of data storage - you request to load or save whole aggregates. Transactions should not cross aggregate boundaries.

Repositories go hand in hand with Aggregates and are usually enforced one to one, as stated by Microsoft (emphasis mine):

It’s important to emphasize again that you should only define one repository for each aggregate root. To achieve the goal of the aggregate root to maintain transactional consistency between all the objects within the aggregate, you should never create a repository for each table in the database.

All of this is important to call out because the code we are going to be implement follows those ideas.


Repository implementation for our “To Do” Domain

The code used for this post is available on Github.

Our “To Do Microservice” implements a To Do Domain and requires persisting data. We will define a type in charge of interacting directly with the persistence layer that also happens to represent a domain entity, this type will implement methods representing concrete business interactions.

The methods to implement depend on the entity we are working on and the aggregate we are trying to represent. In this case we will focus on Task, the repository will define 3 methods:

// TaskRepository defines the datastore handling persisting Task records.
type TaskRepository interface {
	Create(ctx context.Context, description string, priority internal.Priority, dates internal.Dates) (internal.Task, error)
	Find(ctx context.Context, id string) (internal.Task, error)
	Update(ctx context.Context, id string, description string, priority internal.Priority, dates internal.Dates, isDone bool) error
}

Above, I mentioned concrete bussiness interactions but what the TaskRepository defines is literally CRU actions, this is expected for our To Do Domain however the important thing to always remember is to think in terms of our Business Domain and how it is meant to handle those actions, if the CRUD style works for what we are trying to implement that should be fine but, like we mentioned before, we need to make sure we don’t start adding CRUD-like repositories that don’t represent the domain we are implementing.

Our type implementing the TaskRepository interface type is going to be defined as postgresql.Task. The named used for representing the package containing this this type is following the guideline we described before where we the technology name is used, in this case: postgresql.

Behind the scenes postgresql.Task uses the code generated by sqlc to actually interact with the PostgreSQL database to correctly CREATE, SELECT and UPDATE.

Parting words

The Repository Pattern is a powerful way separate the Business Domain Layer and the Persistence Layer, it allows us to indicate exactly how our data moves from and to our datastore while hiding the possibly complexity that this layer could bring.

It’s important to keep in mind that not all entities will require a repository but rather only those that represent a collection of domain entities that happen to need to work as a single unit.

I will expand more about this in future posts, until then, keep it up. I will talk to you next time.


Back to posts