APIs for New Developers: Controllers, Services, and Repositories

When I started my first internship years ago, one of the first projects I worked on was developing an API. One of the most confusing parts to me was understanding the purpose of controllers, services, and repositories. There are thousands of explanations online, but I felt most explanations were difficult to understand. This is a brief, simple explanation I wish I had when I first started.

A Brief Word on Encapsulation

As a new dev, you’ve probably heard the word “encapsulation” tossed around a lot. Maybe you heard it in an object-oriented class or some other explanation on controllers, services, and repositories. You might have some theoretical idea of what it means but are confused about how it applies to API design.

Put simply, encapsulation is just about separating responsibilities. When building a house, you can imagine each person working on a different part of the house encapsulating their responsibility. If I’m building a house and I need plumbing done, I’ll ask a plumber to do it. I don’t need to know how it’s done, I don’t need to know what techniques the plumber follows, and I don’t need to know what tools they’re using. None of that is my responsibility. The plumber encapsulates plumbing, which means I don’t need to worry about the details of how plumbing is done. I just need to know to go to them when I need plumbing work.


Controllers are the first point of contact when a request is made to your API. The controller is responsible for directing data where it needs to go, as well as sending back information to the user that requested it.

To continue the house example, imagine someone needs to have pipes installed in their new house. They request for a construction company to have that done. The construction company, understanding that this is work a plumber needs to do, will ask a plumber to install the pipes. After the plumber is done, the construction company will then let the homeowner know that the pipes have been installed.

The construction company in this situation is the controller. It encapsulates getting a request and directing that data to the appropriate place.


A service handles the dirty work in your API. Any kind of logic and data manipulation is the responsibility of a service. A service is told to do this work either by a controller or other services that need its help.

The plumber in the above example would be a service. While doing their job, the plumber might determine that something needs to be done to the electrical work. The plumber will ask an electrician for help with this. The electrician is also a service. Both the plumber and the electrician encapsulate the work they do.


Repositories are responsible for retrieving and storing data. It doesn’t deal with any business logic. It just gets told what information to store/retrieve and knows exactly where to go to do that. A repository will know what database to connect to, which specific parts of the database to go to in order to input or get data, and how this data should be stored.

Before installing pipes in the house, a plumber first needs to get the correct pipes. They might go to a construction parts store and ask someone working there for the pipes they need. The worker at the parts store will know exactly what pipes they have in stock and where the pipes are stored. The parts store is a repository. It encapsulates the retrieval and storage of construction parts. The plumber doesn’t need to know any of the details about this. They just need to know that if they need pipes, they can ask the store for them.

Saving Time with Controllers, Services & Repositories

Why be so strict in separating responsibilities like this? Newer developers might feel this pattern introduces unnecessary work and boilerplate code. However, in the long run, it makes a developer’s life much easier. Imagine if the homeowner hired a jack-of-all-trades. This is someone who does all of the work, has storage for all of their own parts, and knows exactly what to do and when.

This might make things simpler initially, but imagine if this person can’t do plumbing as well as the homeowner would like. Maybe the pipes they use aren’t up to code. Maybe this person decides to retire in the middle of the job. The homeowner now needs to replace everything the jack-of-all-trades was responsible for completely. If all those responsibilities were separated, the homeowner would only need to replace the specific portion causing an issue.

It might seem counterintuitive at first, but you will find that following a pattern like this will prevent headaches down the line.


Join the conversation

Your email address will not be published. Required fields are marked *