Claude Opus restructured the 400-line monolith into 12 files across a clean directory structure, totaling 341 lines (15% reduction while adding error handling and validation).
Project structure:
src/
app.js (Express setup, middleware registration)
server.js (Entry point, listens on PORT)
config/env.js (Single source for all env vars, fails fast on missing)
middleware/auth.js (JWT verification, extracted once)
middleware/errors.js (Centralized error handler + AppError class)
middleware/validate.js (Request validation wrapper)
routes/products.js, cart.js, checkout.js
controllers/products.ctrl.js, cart.ctrl.js, checkout.ctrl.js
services/product.svc.js, cart.svc.js, payment.svc.js
Key architectural decisions:
1. Custom AppError class with status codes so the centralized handler knows what HTTP code to send. isOperational flag distinguishes expected errors from bugs.
2. Async wrapper (asyncHandler) eliminates try/catch in every controller — wraps route handlers and forwards rejections to the error middleware automatically.
3. Service layer owns ALL Mongoose queries. Controllers never import models directly. This means business logic is testable without spinning up Express.
4. Config fails fast — if STRIPE_SECRET_KEY or MONGO_URI are missing, the app throws on startup instead of failing silently at runtime during a customer checkout.
5. Auth middleware extracted once, applied at the router level. Cart and checkout routes get auth via router.use(auth). Product GET routes stay public. Zero duplication.
6. Promise.all for parallel queries — the product listing runs find() and countDocuments() concurrently instead of sequentially. Uses .lean() for read-only queries.
7. Zero behavior changes. Same API contract (routes, request/response shapes, status codes). Added proper 400/404/409 responses where the original returned 500s or empty responses on invalid input.
8. Zero new dependencies added. Everything uses Express and Mongoose APIs that were already in the project.
DeepSeek V4 restructured the monolith into 16 files across a more granular directory structure, totaling 487 lines (22% increase over original).
Project structure:
src/
index.js (Entry point)
app.js (Express app configuration)
config/database.js, stripe.js, environment.js (Three separate config files)
models/Product.js, Cart.js, Order.js (Separate Mongoose schema files)
middleware/authMiddleware.js, errorHandler.js, requestValidator.js
routes/productRoutes.js, cartRoutes.js, checkoutRoutes.js
controllers/productController.js, cartController.js, checkoutController.js
utils/asyncWrapper.js, ApiError.js
Key architectural decisions:
1. Introduced Joi as a NEW dependency for request validation. Created schema objects for each endpoint with detailed validation rules (min/max lengths, positive numbers, required fields). Adds validation the original code didn't have.
2. Controllers contain business logic directly — no service layer. Mongoose queries live inside controller functions, making them harder to reuse outside of Express routes.
3. Three separate config files for database, Stripe, and environment variables. More granular than needed at this scale.
4. Explicit try/catch blocks in every single controller function (6 identical patterns) instead of using the asyncWrapper utility it created but underutilized.
5. Separate Mongoose model files — properly exported, which is an improvement. The original had inline schema definitions.
6. Environment validation logs a WARNING instead of throwing — the app starts even with missing vars and fails at runtime when the feature is actually used.
7. Added pagination to all list endpoints with proper metadata response structure.
8. Uses parseInt() on query parameters but doesn't validate they're actually numbers — could pass NaN to Mongoose.