惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
博客园_首页
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Vercel News
Vercel News
美团技术团队
D
Docker
WordPress大学
WordPress大学
T
Tailwind CSS Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
The Cloudflare Blog
Y
Y Combinator Blog
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
G
Google Developers Blog
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
月光博客
月光博客
MongoDB | Blog
MongoDB | Blog
S
SegmentFault 最新的问题
GbyAI
GbyAI
Hugging Face - Blog
Hugging Face - Blog
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Why Your Controllers Become 600+ Lines Long (And How a Se...
vedant kale · 2026-06-15 · via DEV Community

When I first started building APIs with Express.js, I made the same mistake many beginners make:

"If it works, just put it in the controller."

So that's exactly what I did.

Database queries? Controller.
Validation? Controller.
Business logic? Controller.
Error handling? Controller. Everything lived in the same file.

At first, this wasn't a problem. The project was small, the controllers were short, and finding bugs was easy. But as the application grew, so did the controllers. A controller that started with 20 lines slowly became 100 lines. Then 300. Then 600+ lines.

At that point, reading the code became difficult. Finding bugs took longer, testing became harder, and making changes felt risky because every responsibility was mixed together in the same file. That's when I discovered the Service Layer pattern.

What Is a Service Layer?

A service layer is responsible for handling your application's business logic.

Think of it this way:

  • Routes decide which endpoint should be called.

  • Controllers handle HTTP requests and responses.

  • Services handle the actual business logic.

Business logic includes things such as:

  • Database queries

  • Validation rules

  • Permission checks

  • User-related operations

  • Payment processing

  • Business rules specific to your application

Instead of putting all of this inside a controller, you move it into dedicated service files. This keeps your controllers small and focused while making your business logic easier to reuse, test, and maintain.

A Simple Analogy

A controller is like a waiter in a restaurant. The waiter takes your order and brings your food to the table. However, the waiter doesn't cook the food. The kitchen does.

In this analogy:

  • The controller is the waiter.

  • The service layer is the kitchen.

The controller receives the request, passes it to the service layer, and returns the result to the client. The actual work happens inside the service.

Without a Service Layer

A typical controller often starts looking like this:

export const getUser = async (req, res) => {
  const id = req.params.id;

  const user = await User.findById(id);

  if (!user) {
    return res.status(404).json({
      message: "User not found",
    });
  }

  return res.json(user);
};

At first glance, this seems fine.

The problem begins when more logic gets added:

  • Validation

  • Authorization checks

  • Database operations

  • Error handling

  • Business rules

Eventually, the controller becomes responsible for everything.

With a Service Layer

Controller:

export const getUser = async (req, res) => {
  try {
    const user = await getUserById(req.params.id);

    return res.json(user);
  } catch (error) {
    return res.status(404).json({
      message: error.message,
    });
  }
};

Service:

export const getUserById = async (id) => {
  const user = await User.findById(id);

  if (!user) {
    throw new Error("User not found");
  }

  return user;
};

Notice the difference.

The controller no longer knows how users are fetched. Its only responsibility is handling the request and returning a response. The service layer contains the business logic required to retrieve a user. This separation becomes increasingly valuable as your application grows.

Common Mistakes to Avoid

1. Putting all business logic inside controllers

Controllers should not become the place where everything happens. Their primary responsibility is handling the request-response cycle.

2. Turning services into another controller

Moving code to a service file is not enough. Services should contain business logic, not HTTP-specific logic such as req, res, or status codes.

3. Ignoring error handling

Service functions can throw errors. Controllers should properly handle those errors and return appropriate responses.

4. Creating services too early for tiny projects

Not every project needs a large architecture from day one. For small applications, simple controllers may be enough. As complexity grows, introducing services becomes more valuable.

Conclusion

The biggest lesson is simple:
Controllers handle requests and responses. Services handle business logic.

Keeping those responsibilities separate makes your code easier to read, test, maintain, and scale as your application grows.

The goal isn't to follow architecture patterns for the sake of it. The goal is to prevent your controllers from becoming 600-line files that nobody enjoys maintaining.