MVC: Model-View-Controller

One of the most popular architectures out there is the model-view-controller, or MVC, architecture. MVC (and related architectures) is everywhere:

  • GitHub: uses Ruby on Rails, an MVC web framework using the Ruby programming language
  • CS 3240: your semester-long project uses Django, an MVC web framework for Python
  • Mobile apps: many frameworks (SwiftUI, Flutter, etc.) use a similar architecture called model-view-viewmodel (we won’t get into the distinctions between MVC and MVVM in this course)

An MVC application has three concerns:

  1. What it knows (data)
  2. How it looks (presentation)
  3. How it acts (handling user interaction)

This is quite similar to a three-layer architecture, which has a layer for business logic instead of one for handling user interaction. Both three-layer and MVC architectures have the same motivation: separate the visual interface of the software from the data management of the application, which is also separated from the actions taken by the application. This separation of concerns allows us to more easily maintain the Single Responsibility Principle, improving the maintainability of our codebase as a result.

Models: What It Knows

A model is just a representation of data. Think of a class and objects belonging to that class – a model works basically the same way. It has fields and properties for storing data. It’ll also have some methods to handle working with instances, such as a method to create a new instance of a model or to get a specific instance (e.g., from a database).

Let’s imagine we’re trying to recreate GitHub. We’ll need a model to represent users. Each user has a name, username, email address, and password, as well as a list of Git repositories that they own, so the model might look something like this:

public class User {
  private String name;
  private String username;
  private String email;
  private String password;
  private Set<Repository> repos;

  public User(String name, String username, String email, String password) {
    this.name = name;
    this.username = username;
    this.email = email;
    this.password = password;
    this.repos = new HashSet<>();
  }

  // the usual getters and setters (omitted for brevity)
}

Models will also have methods for working with instances, such as a method to create a new instance or a method to retrieve a specific instance:

public class User {
  // the stuff from above (omitted for brevity)

  public static User createUser(String name, String username, String email, String password) {
    User newUser = new User(name, username, email, password);
    // save the user somewhere (e.g., a database)
    return newUser;
  }

  public static User getUserByUsername(String username) {
    User result;
    // get the user somehow, or fail if there is no user with this username
    return result;
  }
}

Models may also contain business logic, such as GitHub’s “user contributions” feature that shows on every user’s profile page:

public class User {
  // the stuff from above (omitted for brevity)

  public int numberOfContributions() {
    return this.repos.stream()
      .map(repo -> repo.getCommits())
      .filter(commit -> commit.getAuthor() == this)
      .count();
  }
}

Views: How It Looks

A view is the equivalent of the presentation layer in a three-layer architecture. It controls how the application presents the data represented by models.

For instance, GitHub has a view for users’ profile pages. Let’s say it’s stored in a file called ProfilePage.html. It might look something like this:


<!DOCTYPE html>
<html>
  <head>
    <title>{{ user.getName() }} - GitHub</title>
  </head>
  <body>
    <h1>GitHub</h1>
    <h2>{{ user.getName() }} ({{ user.getUsername() }})</h2>
    <p>Contributions: {{ contributions }}</p>
  </body>
</html>

In this example, we’ll assume that the view is given two pieces of data – user and contributions. Every block surrounded by {{ }} will be replaced with the result of the Java code inside before the final, rendered view is sent back to the requestor. This allows us to work directly with data from our models and dynamically present our content.

Note: we won’t actually be using this templating language in the course. It was made up for this example. If you want to look at an actual language for views, check out our article on FXML.

Controllers: How It Acts

The last part of MVC is the glue that holds it all together. A controller takes user input, grabs the relevant models and views, optionally performs some business logic, and puts everything together to give you a full interactive experience.

Here’s how it works for our GitHub recreation: Let’s say someone tries to access a user’s profile page. This information gets sent to a controller, which has a method for every different user interaction:

public class UserController {
  private static boolean isProfilePage(String url) {
    // we have some code here to determine if the URL is for a profile page
  }

  public static View handleWebRequest(String url) {
    if (isProfilePage(url)) {
      return handleUserProfile(url);
    }
    // we have other actions to take for different URLs
  }
}

The handleUserProfile method needs to get the user from the User model, get the ProfilePage.html view, get the number of contributions that the user has, and let the view render itself with this information before the output is sent back to the requestor:

public class UserController {
  // the stuff from above (omitted for brevity)

  private static User getUserFromURL(String url) {
    String[] urlParts = url.split("/");
    String username = urlParts[urlParts.length - 1];
    return User.getUserByUsername(username);
  }

  private static View handleUserProfile(String url) {
    User user = getUserFromURL(url);
    Integer numberOfContributions = Integer.valueOf(user.numberOfContributions());
    // we'll pass all the data the view needs as a map called "context"
    Map<String, Object> context = new HashMap<>();
    context.put("user", user);
    context.put("contributions", numberOfContributions);
    return View.getView("ProfilePage.html", context);
  }
}

That’s the entire MVC process! Now, we can add additional functionality without touching existing parts of our application by simply creating or extending models, views, and controllers.


Previous submodule: