On this page
Three questions about one requestI want to read the operation without reading the transportA thin service is not automatically a problemRepository does not have to mean databaseWhat I look for in a reviewA route can start with a few lines: read an input, fetch something, send a response. Then it needs another validation check, a second data source, and a slightly different response for one caller. The handler still works, but reading it takes more effort.
My backend work includes Node.js and Express APIs organized into controllers, services, and repositories. What interests me about that structure is where a decision belongs. The folder names are the easy part.
Three questions about one request
When I look at a request flow, I find these questions useful:
- What does the HTTP client send, and what should it receive?
- What operation is the application trying to perform?
- Where does the information come from?
Those questions give me a reason for each boundary.
The controller deals with the request and response. A service expresses the application operation. A repository handles retrieval or persistence. Validation middleware can reject an invalid request before the controller continues.
For a simple catalogue lookup, the conceptual flow might look like this:
HTTP request
-> validate query parameters
-> controller
-> catalogue service
-> catalogue repository
-> HTTP response
This is an illustrative example, not a diagram of an employer's system.
I want to read the operation without reading the transport
Suppose a catalogue operation checks whether a category exists, retrieves matching items, and applies a visibility rule. I want those application decisions to be understandable without following response methods and status codes through the same function.
Likewise, I do not want a data-access method deciding the wording of an HTTP error message. The caller may change, even when the underlying operation stays the same.
This separation also gives me smaller questions to ask during debugging. Did the request reach the controller? Did the application reject it intentionally? Did retrieval fail? An error can cross these boundaries, but the responsibility for interpreting it should be clear.
Express has a specific error-handling mechanism. I keep that transport-level handling distinct from the application's decision about whether an operation is valid.
A thin service is not automatically a problem
Sometimes a service only delegates to a repository. I do not think that needs an elaborate justification every time. A consistent boundary can be useful when nearby operations contain more logic.
But an extra function is not free. It adds another name to understand and another file to navigate. If every layer repeats the same arguments, catches the same error, and changes nothing, I would question whether the structure is helping.
I would rather keep a service small than invent logic to make it look necessary. I would also avoid introducing a generic service framework before the application has repeated behavior worth sharing.
Repository does not have to mean database
A repository is a boundary around access to information. The implementation could read a database, call another service, or filter an in-memory collection.
That distinction matters when explaining an application. A repository directory does not prove that a particular database is active. I need to follow the implementation before making that claim.
The useful question is whether the rest of the application can ask for the information it needs without depending on the retrieval details.
What I look for in a review
I would start with one request and follow it all the way through. If I have to open several files, each file should answer a different question. I would check whether HTTP objects reach into data-access code, whether domain rules are scattered between handlers, and whether error handling preserves enough context to investigate a failure.
A small application may need fewer boundaries. A growing application may benefit from more. My preference is to make the flow easy to explain, then let the structure follow the work it actually does.