Marcy Lab School Docs
  • Welcome
  • Student Guidelines & Policies
    • Student Handbook
    • AI Policy
    • Academic Calendar
  • Environment Setup
    • Local Environment Setup - Mac
    • Local Environment Setup - Windows
    • GitHub Setup
    • Postgres Setup
  • How-Tos
    • How To Code at Marcy: Code Style Guide
    • How to Do Short Response and Coding Assignments
    • How to Debug
    • How to PEDAC
    • How to Create A GitHub Organization and Scrumboard
    • How to Create Projects with Vite
    • How to Deploy on GitHub Pages
    • How to Deploy on Render
    • How to Test your API with Postman
  • Mod 0 - Command Line Interfaces, Git, and GitHub
    • Overview
    • 1. Command Line Interfaces
    • 2. Git & GitHub
    • 3. Git Pulling & Merging
    • 4. Git Branching & PRs
  • Mod 1 - JavaScript Fundamentals
    • Overview
    • 1. Intro to Programming
    • 2. Errors
    • 3. Node & Node Modules
    • 4. Variables, Functions & String Methods
    • 5. Control Flow, typeof, and Math
    • 6. Loops
    • 7. Arrays
    • 8. Objects
    • 9. Higher Order Functions: Callbacks
    • 10. Higher Order Functions: Array Methods
    • 11. Regex
  • Mod 2 - HTML, CSS & the DOM
    • Overview
    • 1. HTML
    • 2. CSS
    • 3. Accessibility (a11y)
    • 4. The Document Object Model (DOM) API
    • 5. Events
    • 6. Forms
    • 7. The Box Model and Positioning
    • 8. Flexbox
    • 9. Grid & Media Queries
    • 10. ESModules
    • 11. Vite
    • 12. LocalStorage
  • Mod 3 - Async & APIs
    • Overview
    • 1. Promises
    • 2. Fetch
    • 3. Building a Fetching App
    • 4. Async & Await
    • 5. A Generic Fetch Handler
  • Mod 4 - Project Week!
    • Important How Tos and Guides
      • How to Create a GitHub Organization and Scrum Board
      • How To Start a Project with Vite
      • How To Deploy a Project with GitHub Pages
    • Project Week Overview
    • Agile Methodologies
    • Deliverables & Milestones
    • Technical Requirements Checklist
    • Free API List
    • Collaborative GitHub
  • Mod 5 - Object-Oriented Programming
    • Overview
    • 1. Intro to OOP, Encapsulation, Factory Functions, and Closure
    • 2. Classes
    • 3. Private & Static
    • 4. UML Diagrams & Has Many/Belongs To Relationships
    • 5. Challenge: Implementing Has Many/Belongs To
    • 6. Inheritance
    • 7. Polymorphism
    • 8. Review and Practice
    • MDN: Object Prototypes
  • Mod 6 - Data Structures & Algorithms
    • Overview
    • Important How Tos and Guides
      • How to Debug
      • How to PEDAC
    • 1. Nodes & Linked Lists
    • 2. Singly & Doubly Linked Lists
    • 3. Stacks & Queues
    • 4. Recursion
    • 5. Trees
  • Mod 7 - React
    • Overview
    • Important How Tos and Guides
      • How to Create Projects with Vite
      • How to Deploy on GitHub Pages
    • 1. Intro to React
    • 2. Events, State, and Forms
    • 3. Fetching with useEffect
    • 4. React Router
    • 5. Building a Flashcards App
    • 6. React Context
    • 7. Global Context Pattern
  • Mod 8 - Backend
    • Overview
    • Important How Tos and Guides
      • How to Deploy on Render
      • How to Test your API with Postman
      • Postgres Setup
    • 1. Intro to Express
    • 2. Building a Static Web Server with Middleware
    • 3. Securing API Keys and Environment Variables
    • 4. RESTful CRUD API
    • 5. Model-View-Controller Architecture
    • 6. SQL and Databases
    • 7. JOIN (Association) SQL Queries
    • 8. Knex
    • 9. Your First Fullstack App!
    • 10. Migrations & Seeds
    • 11. Schema Design & Normalization
    • 12. Hashing Passwords with Bcrypt
    • 13. React Express Auth Template Overview
  • Mod 9 - Civic Tech Hackathon
    • Overview
    • Rubric
  • Mod 10 - Capstone
    • Overview
Powered by GitBook
On this page
  • Organization and Separation of Concerns
  • The Model-View-Controller (MVC) Architecture
  • Implementing a Model for MVC
  • Server Organization
  • Build a Model
  • Challenge
  1. Mod 8 - Backend

5. Model-View-Controller Architecture

Previous4. RESTful CRUD APINext6. SQL and Databases

Last updated 1 month ago

Follow along with code examples !

As your application grows in scale and scope, it is important to have a consistent approach for organizing its many components.

In this lesson, we'll learn how to implement one of the most popular patterns called the Model-View-Controller Architecture.

Table of Contents:

Organization and Separation of Concerns

In the last lesson, we built a RESTful API that lets users manage a list of fellows. They can:

  • (Create) Add a new fellow to the list

  • (Read) Get all fellows

  • (Read) Get a single fellow

  • (Update) Change the name of a fellow

  • (Delete) Remove a fellow from the list

In that application, all of the logic was built into the serer/index.js file. While we have some organization within the file, separating the concerns of one file into multiple files will enable our application to scale without becoming a "monolith"

In software development, a "code monolith" refers to a single, large, and typically tightly coupled codebase that contains all the application's components, often making it difficult to scale, maintain, and deploy

The Model-View-Controller (MVC) Architecture

While there are many approaches for organization and separation of concerns, one highly popular approach is called the Model-View-Controller (MVC) Architecture.

This architecture pattern organizes our code into three distinct pieces:

  • The Models are responsible for storing and managing the data of an application. They provide an interface (a set of methods) for interacting with that data in a predictable manner.

  • The Views are responsible for rendering the data of an application. They provide user-interfaces with buttons, forms, and other components that allow the user to see and request changes to the data.

  • The Controllers are responsible for managing interactions between the views and models. They process user inputs from the views, invoke the appropriate methods of the models, and send response back to the views to be updated.

Often times, it can be hard to implement your application such that it strictly adheres to any one framework or architecture. Keep in mind that architectures like MVC present an ideal to strive for, not a strict pattern that must be followed at all times.

What separate components of this architecture does our application already have?

We have a React application acting as the view component.

We have the Express server application acting as the controllers AND as the model. We need to separate the controller from the model.

Implementing a Model for MVC

With our current application structure, we already have clear separation between the views (our frontend React application) and the controllers (our Express endpoints/controllers).

However, our controllers and model are intertwined since our controllers directly interact with the database (the fellows array). That is, they both handle the logic of parsing request inputs AND handle the logic of managing the data.

// Our "Database"
const fellows = [
  { name: 'Carmen', id: getId() },
  { name: 'Reuben', id: getId() },
  { name: 'Maya', id: getId() },
];

// Our Controllers
const createFellow = (req, res) => {
  // Parse Inputs
  const { fellowName } = req.body;

  // Send Response
  if (!fellowName) {
    return res.status(400).send({ message: "Invalid Name" });
  }
  
  // Create a new fellow and add it to the "database"
  const newFellow = {
    name: fellowName,
    id: getId()
  }
  fellows.push(newFellow)

  // Send Response
  res.status(201).send(newFellow);
}

const serveFellow = (req, res) => {
  // Parse Inputs
  const { id } = req.params;

  // Find the fellow in the "database"
  const fellow = fellows.find(fellow => fellow.id === Number(id));

  // Send Response
  if (!fellow) {
    return res.status(404).send({ 
      message: `No fellow with the id ${id}`
    });
  }

  res.send(fellow);
};

// ... and more...

We need to create a separate model that focuses solely on managing the friends database and provides methods for our controllers to use.

Server Organization

To create an MVC architecture, take a moment and build the following file structure for your server application.

server/
├── index.js
├── controllers/
│   └── fellowControllers.js
├── models/
│   └── Fellow.js
└── utils/
    ├── getId.js
    └── handleFetch.js
  • index.js builds the app, configures middleware, and sets the endpoints. However, the controllers are now imported.

  • controllers/fellowControllers.js defines all of the controllers for endpoints relating to fellow data. Each set of data should have its own controllers file.

  • models/Fellow.js defines a model for managing fellow data. This model is used exclusively by the fellow controllers. Each set of data managed by the server should have its own model.

By separating our code in this way, we show the separate "layers" of the application.

Before we build a model, do the following:

  • Move all of your controller functions into the controllers/fellowControllers.js file

  • Export them as an object using module.exports = { /* list controller methods*/ }

  • Import the collection of controllers into server/index.js using require(./controllers/fellowControllers.js)

Build a Model

To build a separate model layer to handle only data management logic, we will:

  • Make a models/Fellow.js file.

  • Inside, export a Fellow with static methods for each action needed by the controllers.

  • Move the fellows "database" inside so the only way to access its data is through the model's interface.

Note that there is no constructor() function here! This may seem odd at first, but consider interfaces like Math. These interfaces just provide static methods and properties like Math.random() and Math.PI. You never invoke new Math() to generate an instance of Math

server/models/Fellow.js
// Restrict access to our mock "database" to just the Model
const fellows = [
  { name: 'Carmen', id: getId() },
  { name: 'Reuben', id: getId() },
  { name: 'Maya', id: getId() },
];

class Fellow {
  // Create and add the new fellow to the "database" (the fellows array)
  static create(name) {
    const newFellow = {
      name,
      id: getId()
    }
    fellows.push(newFellow);
    return newFellow;
  }

  // Get all values from the "database"
  static getAll() {
    return [...fellows];
  }

  // Get one value from the "database"
  static find(id) {
    return fellows.find((fellow) => fellow.id === id);
  }

  // Update one value from the "database"
  static editName(id, newName) {
    const fellow = Fellow.find(id);
    if (!fellow) return null;
    fellow.name = newName;
    return fellow;
  }

  // Delete one value from the "database"
  static delete(id) {
    const fellowIndex = fellows.findIndex((fellow) => fellow.id === id);
    if (fellowIndex < 0) return false;

    fellows.splice(fellowIndex, 1);
    return true;
  }
}

module.exports = Fellow;

With the Fellow model, we now leave managing the data to the model and let the control just focus on parsing the request and sending the response.

server/controllers/fellowControllers.js
const Fellow = require('../models/Fellow.js');)

const createFellow = (req, res) => {
  const { fellowName } = req.body;
  if (!fellowName) {
    return res.status(400).send({ message: "Invalid Name" });
  }

  const newFellow = Fellow.create(fellowName);
  res.send(newFellow);
};

// Use `Fellow.find` to get the desired fellow
const serveFellow = (req, res) => {
  const { id } = req.params;
  const fellow = Fellow.find(Number(id));

  if (!fellow) {
    return res.status(404).send({ 
      message: `No fellow with the id ${id}`
    });
  }
  res.send(fellow);
};

Challenge

Build a Song model and a server application for maintaining a playlist. Each song should have an id, a title, and an artist (at minimum). The model should provide an interface for:

  • Creating a new song

  • Getting all songs

  • Getting a single song

  • Updating the title or artist of a song

  • Deleting a song

Then, create an endpoint and a controller for each of these pieces of functionality. The endpoints should follow REST conventions and should all begin with /api

Finally, build a frontend react application that can interact with the songs API that you've built. It should be able to:

  • Create: Add a new song to the list.

  • Read: Display a list of all songs.

  • Read: Display a single song.

  • Update: Update a single songs's title or artist.

  • Delete: Delete a single song.

Here is a recommended React Router page structure for your React app:

  • /: The homepage which includes:

    • Form for creating a new song

    • List of all songs

  • /songs/:id: The details of a single song which includes

    • The title, artist, and ID of the song

    • A form to submit a new title or artist for the song

    • A button to delete the song from the list

here
Organization and Separation of Concerns
The Model-View-Controller (MVC) Architecture
Implementing a Model for MVC
Server Organization
Build a Model
Challenge
The view sends requests with user inputs to the controller with updates the model. New data is returned to the controller which sends that data in a response to the view.
Controllers now use the Fellow Model interface to update the "database" before sending a response back to the client.