Tutorials Logic, IN info@tutorialslogic.com

Express.js Controllers, Services, and Clean Architecture: Separate Request Code From Business Rules

Express.js Controllers, Services, and Clean Architecture

An Express route becomes easier to maintain when it does not try to do everything itself.

Controllers should care about request and response details. Services should care about the business operation being performed.

This separation helps beginners escape giant route files and helps professionals keep the codebase testable as it grows.

The goal is not to worship architecture diagrams. The goal is to keep responsibilities from collapsing into one place.

What Controllers Should Own

A controller sits near the edge of the system. It reads route parameters, body data, query values, authenticated user context, and then decides how to translate the service result into an HTTP response.

That means controllers should stay close to the web layer. They are not the best home for large business rules, pricing decisions, permission rules, or reusable workflows.

  • Parse request inputs.
  • Call the appropriate service.
  • Translate results into status codes and response bodies.

What Services Are Better At

Services are useful when the same business behavior might be used by more than one route, more than one transport, or more than one future feature. They make reasoning easier because the business action gets a real name instead of hiding inside an endpoint body.

A service can still be small. It does not need to be an elaborate class hierarchy. Sometimes a well-named function that handles one business operation is enough.

  • Keep business decisions out of raw request code.
  • Use services for operations that need to be reused or tested independently.
  • Name services after the action, not the technical layer.

The Clean Architecture Idea Without The Ceremony

People often overcomplicate clean architecture. The practical version is simple: the outer web layer should not own your core business rules, and infrastructure details should not leak everywhere.

If your route handler can be read in a few seconds and your business rule can be tested without spinning up the whole server, you are already moving in a healthy direction.

  • Separate what is web-specific from what is business-specific.
  • Keep data access behind a clear boundary when possible.
  • Avoid ceremony that adds folders but not clarity.

Thin controller, named service

This pattern is easier to maintain than a long endpoint body that mixes every concern.

Thin controller, named service
Controller: read request -> call createProjectService -> map success to 201 or failure to 400/403
Service: validate business rules -> persist data -> return result
  • The controller speaks HTTP.
  • The service speaks business behavior.
  • The split becomes especially valuable once more endpoints appear.
Key Takeaways
  • I can describe the difference between a controller and a service.
  • I know why business logic inside route files becomes painful over time.
  • I understand that clean architecture should improve clarity, not only add ceremony.
  • I can describe a thin-controller pattern in plain language.
Common Mistakes to Avoid
Putting every business decision directly inside controllers.
Creating many layers with impressive names but unclear responsibility.
Separating files without actually separating concerns.

Practice Tasks

  • Take one create-order endpoint and split its concerns between controller and service.
  • Write a short naming rule for services in your project.
  • Identify one place where infrastructure knowledge is leaking too far into business code.

Frequently Asked Questions

Not necessarily. Use the simplest form that keeps business behavior reusable and understandable.

When it mixes validation, business decisions, data access, and response mapping in a way that is hard to read or test independently.

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.