In this lesson, you will learn how to decouple your business logic from HTTP request handlers by moving complex calculations into dedicated service classes.
The Problem: The "Fat" Controller
In many beginner applications, request handlers (like those in Express or Fastify) become cluttered. They handle everything: parsing request bodies, validating inputs, performing heavy calculations, interacting with the database, and formatting the response. This makes code hard to test and impossible to reuse. If you need to perform the same "Price Calculation" logic in both an API route and a CLI script, you would currently have to copy-paste the code.
The Solution: Service Layer Abstraction
A Service Layer is a dedicated space for your business logic. By moving complex operations into "Service" classes, your request handlers become thin "traffic controllers" whose only job is to receive a request, call the appropriate service, and send back the result.
Consider this example where we move a discount calculation out of the handler:
Before (Fat Handler):
app.post('/checkout', (req, res) => {
const { total, items } = req.body;
// This logic is trapped inside the route!
const discount = items.length > 5 ? total * 0.1 : 0;
res.json({ finalPrice: total - discount });
});
After (Using a Service):
class PricingService {
calculateDiscount(total, items) {
return items.length > 5 ? total * 0.1 : 0;
}
}
// In your route:
app.post('/checkout', (req, res) => {
const pricingService = new PricingService();
const discount = pricingService.calculateDiscount(req.body.total, req.body.items);
res.json({ finalPrice: req.body.total - discount });
});
Why This Matters
By abstracting this into PricingService, you gain three main advantages:
- Reusability: You can import
PricingServiceinto other parts of your application (like a background cron job) without worrying about HTTP request objects. - Testability: You can write unit tests for the
PricingServicein isolation, without needing to mock complex request/response objects. - Readability: Your route handlers now describe what the application does, while your services describe how it does it.
Key Takeaway: Moving business logic into a Service Layer separates the "how" from the "where," making your code cleaner, testable, and reusable across different parts of your application.