Episode 3 — NodeJS MongoDB Backend Architecture / 3.3 — Backend Architectures

3.3 — Exercise Questions: Backend Architectures

Practice questions covering software architecture fundamentals, MVC, Service layers, SOA, microservices, event-driven, and serverless patterns. Work through these to solidify your understanding before moving on.


Home


Section A: Architecture Fundamentals (Questions 1-8)

Question 1: Architecture vs. Design

Explain the difference between software architecture and software design. Give one example of each for a social media application.


Question 2: Separation of Concerns

A teammate writes this single Express route handler. Identify all the concerns mixed together and list which layer each belongs to.

app.post('/api/orders', async (req, res) => {
  if (!req.body.items || req.body.items.length === 0) {
    return res.status(400).json({ error: 'Items required' });
  }
  let total = 0;
  for (const item of req.body.items) {
    const product = await db.collection('products').findOne({ _id: item.id });
    total += product.price * item.qty;
  }
  if (total > 100) total = total * 0.9;
  await db.collection('orders').insertOne({
    userId: req.user.id,
    items: req.body.items,
    total,
    createdAt: new Date()
  });
  await transporter.sendMail({
    to: req.user.email,
    subject: 'Order Confirmed',
    text: `Your total is $${total}`
  });
  res.status(201).json({ message: 'Order created', total });
});

Question 3: Why Architecture Matters

List four ways that poor architecture negatively affects a growing development team. Be specific.


Question 4: Monolith Advantages

Give three situations where a monolithic architecture is the correct choice. Explain why for each.


Question 5: Architecture Documentation

You are starting a new project with 3 developers. Write a short Architecture Decision Record (ADR) explaining why you chose MVC with a Service layer over a microservices approach.


Question 6: Scalability Impact

Explain, with a concrete example, how separating your code into layers (Routes, Controllers, Services, Models) makes it easier to add caching to your application without rewriting existing code.


Question 7: The Architecture Spectrum

Place these five architectures in order from simplest to most complex: Serverless, Monolith, Microservices, Modular Monolith, SOA. Briefly justify the ordering.


Question 8: Team Structure Impact

A company has 4 teams of 5 developers each. Explain why a modular monolith organized by feature (users module, orders module, products module, payments module) might be better than a microservices architecture for this team.


Section B: MVC Architecture (Questions 9-18)

Question 9: MVC Components

Fill in this table with what each MVC component is responsible for and what it should NOT do:

ComponentResponsible ForShould NOT Do
Model??
View??
Controller??

Question 10: Identify the Layer

For each line of code, identify which MVC layer it belongs in (Model, View/Response, Controller, or Service):

a) const hashedPassword = await bcrypt.hash(password, 12);
b) res.status(201).json({ status: 'success', data: user });
c) const { name, email } = req.body;
d) if (user.subscription === 'premium') discount = 0.2;
e) userSchema.pre('save', function() { ... });
f) router.get('/users/:id', userController.getUser);
g) await sendWelcomeEmail(user.email);
h) const user = await User.findById(id).populate('orders');

Question 11: Request Flow

Draw (using ASCII art or describe step-by-step) the complete flow when a client sends POST /api/products with a JSON body to create a new product. Include every layer the request passes through.


Question 12: Fat Controller Refactor

Refactor this fat controller into proper MVC layers. Write the code for the Model method, the Service function, and the thin Controller.

exports.registerUser = async (req, res) => {
  const { name, email, password, role } = req.body;
  
  if (password.length < 8) {
    return res.status(400).json({ error: 'Password too short' });
  }
  
  const existingUser = await User.findOne({ email });
  if (existingUser) {
    return res.status(400).json({ error: 'Email taken' });
  }
  
  const salt = await bcrypt.genSalt(12);
  const hashedPassword = await bcrypt.hash(password, salt);
  
  const user = await User.create({
    name,
    email,
    password: hashedPassword,
    role: role === 'admin' ? 'user' : role  // prevent self-admin
  });
  
  const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
    expiresIn: '7d'
  });
  
  await transporter.sendMail({
    to: email,
    subject: 'Welcome!',
    text: `Hi ${name}, welcome to our app!`
  });
  
  res.status(201).json({ user, token });
};

Question 13: Folder Structure

Create a complete folder structure for an Express MVC project that has three resources: Users, Products, and Orders. Include routes, controllers, services, models, middlewares, and config directories.


Question 14: Model Responsibilities

Write a Mongoose model for a BlogPost with the following requirements:

  • Title (required, max 200 chars)
  • Content (required)
  • Author (reference to User model)
  • Tags (array of strings)
  • Status (enum: draft, published, archived)
  • A pre-save hook that generates a URL-friendly slug from the title
  • A method that checks if the post is editable (only drafts can be edited)

Question 15: Controller Error Handling

What is wrong with this controller's error handling? Rewrite it using the next(error) pattern with a global error handler.

exports.getProduct = async (req, res) => {
  try {
    const product = await Product.findById(req.params.id);
    if (!product) {
      return res.status(404).json({ error: 'Not found' });
    }
    res.json(product);
  } catch (error) {
    console.log(error);
    res.status(500).json({ error: 'Server error' });
  }
};

Question 16: Testing Layers

Explain how you would test the following without connecting to a real database:

  1. A Mongoose model's validation rules
  2. A service function that calls the model
  3. A controller function that calls the service

For each, describe what you would mock and what you would assert.


Question 17: MVC Drawbacks

List three drawbacks of using MVC for a very small project (e.g., a single-page API with 2 endpoints). Is there a simpler alternative?


Question 18: Separation of Concerns Violation

This code has Model logic leaking into the Controller. Identify the violations and explain where each piece of logic should live.

exports.updateUserRole = async (req, res) => {
  const user = await User.findById(req.params.id);
  
  // Business rule: only superadmins can promote to admin
  if (req.body.role === 'admin' && req.user.role !== 'superadmin') {
    return res.status(403).json({ error: 'Only superadmins can promote' });
  }
  
  // Business rule: cannot demote yourself
  if (req.user.id === req.params.id && req.body.role !== user.role) {
    return res.status(400).json({ error: 'Cannot change your own role' });
  }
  
  user.role = req.body.role;
  user.updatedBy = req.user.id;
  await user.save();
  
  res.json(user);
};

Section C: MVC with REST APIs (Questions 19-25)

Question 19: View in REST APIs

Explain why we say the "View" in a REST API is the JSON response. What is the equivalent of "rendering a template" when building an API?


Question 20: Response Format Design

Design a consistent JSON response format for the following scenarios:

  1. Successfully fetching a list of 25 products (page 2 of 5)
  2. Successfully creating a new user
  3. Validation error when creating a user (missing email and short password)
  4. Server error (database connection failed)

Question 21: Service Layer Justification

A colleague argues that the Service layer is unnecessary overhead: "Why not just put the logic in the Controller or the Model?" Give three concrete reasons why the Service layer is worth the extra files.


Question 22: CRUD Implementation

Write the complete code (Route, Controller, Service, Model) for a DELETE /api/comments/:id endpoint that:

  • Requires authentication
  • Only the comment author or an admin can delete
  • Uses soft delete (sets isDeleted: true)
  • Returns 204 on success

Question 23: Error Bubbling

Trace how an error flows through the layers when a user tries to create a product with a negative price. Start from the Controller, go through the Service and Model, and show how it reaches the global error handler.


Question 24: Thin Controller Test

This controller is "thin." Write a unit test for it by mocking the productService.

exports.getProducts = async (req, res, next) => {
  try {
    const result = await productService.getProducts(req.query);
    res.status(200).json({
      status: 'success',
      results: result.products.length,
      data: { products: result.products }
    });
  } catch (error) {
    next(error);
  }
};

Question 25: Custom AppError

Write a custom AppError class that:

  • Extends the built-in Error class
  • Accepts a message and statusCode
  • Sets status to 'fail' for 4xx codes and 'error' for 5xx codes
  • Sets isOperational to true (to distinguish from programming errors)
  • Captures the stack trace properly

Then show how you would use it in a service function.


Section D: SOA and Other Architectures (Questions 26-33)

Question 26: SOA vs. Microservices

What are the three key differences between SOA and microservices? Why did microservices evolve from SOA?


Question 27: Inter-Service Communication

You have a User Service and an Order Service. When a new order is placed, the Order Service needs the user's shipping address. Compare two approaches:

  1. Synchronous: Order Service makes an HTTP GET to User Service
  2. Asynchronous: Order Service has a cached copy of shipping addresses, updated via events

List the pros and cons of each.


Question 28: Event-Driven Design

Design an event-driven system for the following scenario: When a user signs up, the system must:

  1. Send a welcome email
  2. Create a default profile
  3. Grant 100 bonus points
  4. Log the signup for analytics

Write the event name, the producer, and the four consumers. Show the code structure (no full implementation needed).


Question 29: Serverless Trade-offs

A team wants to migrate their entire Express REST API (12 endpoints, always-on traffic) to AWS Lambda. List three reasons this might be a bad idea and two cases where serverless would be a better fit.


Question 30: Distributed Monolith

What is a "distributed monolith"? Why is it considered worse than both a regular monolith and proper microservices? Give a specific example of how this happens.


Question 31: Extraction Strategy

You have a monolith with these modules: Users, Products, Orders, Search, Notifications. The Search module receives 5x more traffic than everything else and uses Elasticsearch. Which module would you extract first into its own service? Justify your answer with at least three reasons.


Question 32: Architecture Matching

Match each project description to the most appropriate architecture:

ProjectOptions: Monolith, MVC+Service, Microservices, Event-Driven, Serverless
A personal blog with 5 pages?
An image thumbnail generator triggered by file uploads?
A banking platform with 200 developers?
A real-time chat application?
A startup MVP with 3 developers?
An IoT sensor data pipeline processing millions of events/day?

Question 33: Modular Monolith Structure

Design a folder structure for a modular monolith e-commerce application with these modules: Users, Products, Orders, Payments, and Shipping. Each module should have its own routes, controllers, services, and models. Show how modules communicate through defined interfaces rather than directly importing each other's internals.


Bonus Section: Coding Challenges (Questions 34-37)

Question 34: Build an Event Bus

Write a simple in-memory event bus class in Node.js that supports:

  • on(event, callback) - register a listener
  • emit(event, data) - fire an event
  • off(event, callback) - remove a listener
  • once(event, callback) - listener that auto-removes after one call

Write tests for each method.


Question 35: API Gateway Simulation

Build a simple API gateway using Express that:

  • Accepts all requests on port 3000
  • Routes /api/users/* requests to http://localhost:3001
  • Routes /api/products/* requests to http://localhost:3002
  • Adds a X-Request-ID header to all proxied requests
  • Logs every request with timestamp and target service

Question 36: Complete MVC Feature

Build a complete "bookmark" feature for a reading app using MVC with Service layer:

  • Users can bookmark articles
  • Users can list their bookmarks (paginated)
  • Users can remove a bookmark
  • A user cannot bookmark the same article twice

Write: Model, Service, Controller, Routes, and at least 3 test cases.


Question 37: Architecture Comparison Essay

Write a 300-word comparison of how these three companies might architect the same feature (user authentication):

  1. A solo developer building an MVP
  2. A 20-person startup
  3. A company with 500 engineers

Cover: where the auth code lives, how it deploys, how it scales, and what happens when it needs to change.


Answer Key Guidance

For each question, check your answer against these criteria:

CriterionCheck
Correct layer assignmentDoes each piece of code live in the right layer?
Separation of concernsDoes each layer have exactly one responsibility?
TestabilityCan each layer be tested independently?
No HTTP in services/modelsDo services and models avoid req, res, next?
Error propagationDo errors bubble up to a centralized handler?
Practical trade-offsDo you acknowledge both pros and cons of each approach?

Home