MVC

 History, Misconceptions and Future!

Milko Kosturkov

  • Developer for over 13 years
  • fullstack
  • bachelor in Electronics
  • master in IT
  • contractor
  • Ty's Software
  • Bulgaria PHP Conference 2019 Organizer

Fields I've worked in

SMS and Voice services

MMO Games

local positioning systems 

TV productions

Healthcare

e-commerce

websites

SEO

online video

Why this talk?

  • We need to know our past
  • Mitigate confusion
  • Present a new and better concept

Flashback

  • invented in 1979
  • by Trygve Reenskaug
  • while with Xerox
  • NOT FOR THE WEB
  • for GUI applications
  • with Smalltalk - 80
  • for the "Dynabook"

By Trygve Reenskaug - Trygve Reenskaug, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=31168223

MVC was conceived as a general solution to the problem of users controlling a large and complex data set.

 A Thing

According to Trygve Reenskaug

Something that is of interest to the user. It could be concrete, like a house or an integrated

circuit. It could be abstract, like a new idea or opinions about a paper. It could be a whole,

like a computer, or a part, like a circuit element

Model

According to Trygve Reenskaug

Models represent knowledge. A model could be a single object (rather uninteresting), or it

could be some structure of objects.

There should be a one-to-one correspondence between the model and its parts on the one hand, and the represented world as perceived by the owner of the model on the other hand

View

According to Trygve Reenskaug

A view is attached to its model (or model part) and gets the data necessary for the presentation

from the model by asking questions...

A view is a (visual) representation of its model. It would ordinarily highlight certain attributes of the model and suppress others

... the view will therefore have to know the semantics of the attributes of the model it represents.

Controllers

According to Trygve Reenskaug

A controller is the link between a user and the system. It provides the user with input by

arranging for relevant views to present themselves in appropriate places on the screen. It

provides means for user output by presenting the user with menus or other means of giving

commands and data.

A controller should never supplement the views...

Conversely, a view should never know about user input, such as mouse operations and

keystrokes.

MVC

MVC

MVC

vs

Web MVC

MVC vs Web MVC

Application lyfecycle

  • MVC
    • long run duration
    • continuous input (clicks, keyboard strokes)
    • continuous output (multiple view updates)
  • Web MVC
    • short run duration
    • one time input
    • one time output

MVC vs Web MVC

Models

  • MVC
    • hold the state of the app
    • describe the behavior of the app
    • are reflection of reality
  • Web MVC
    • DBAL
    • DB table mappings

MVC vs Web MVC

Views

  • MVC
    • know the models
    • represent the state of their respective models
  • Web MVC
    • know the models*
    • represent the state of their respective models

MVC vs Web MVC

Controllers

  • MVC
    • respond to user input by calling methods on models
    • select and arrange one or more views
  • Web MVC
    • respond to user input
    • contain a good portion of the application's behavior
    • validate input
    • hold dependencies
    • select one view
    • manipulate models (DB)
    • set HTTP headers
    • communicate with external services
    • ......

Why is Web MVC called MVC at all???

JSP Model 1

<html>
<head>
  <title>Book Query</title>
</head>
<body>
  <h1>Another E-Bookstore</h1>
  <h3>Choose Author(s):</h3>
  <form method="get">
    <input type="checkbox" name="author" value="Tan Ah Teck">Tan
    <input type="checkbox" name="author" value="Mohd Ali">Ali
    <input type="checkbox" name="author" value="Kumar">Kumar
    <input type="submit" value="Query">
  </form>
 
  <%
    String[] authors = request.getParameterValues("author");
    if (authors != null) {
  %>
  <%@ page import = "java.sql.*" %>
  <%
      Connection conn = DriverManager.getConnection(
          "jdbc:mysql://localhost:8888/ebookshop", "myuser", "xxxx"); // <== Check!
      // Connection conn =
      //    DriverManager.getConnection("jdbc:odbc:eshopODBC");  // Access
      Statement stmt = conn.createStatement();
 
      String sqlStr = "SELECT * FROM books WHERE author IN (";
      sqlStr += "'" + authors[0] + "'";  // First author
      for (int i = 1; i < authors.length; ++i) {
         sqlStr += ", '" + authors[i] + "'";  // Subsequent authors need a leading commas
      }
      sqlStr += ") AND qty > 0 ORDER BY author ASC, title ASC";
 
      // for debugging
      System.out.println("Query statement is " + sqlStr);
      ResultSet rset = stmt.executeQuery(sqlStr);
  %>
      <hr>
      <form method="get" action="order.jsp">
        <table border=1 cellpadding=5>
          <tr>
            <th>Order</th>
            <th>Author</th>
            <th>Title</th>
            <th>Price</th>
            <th>Qty</th>
          </tr>
  <%
      while (rset.next()) {
        int id = rset.getInt("id");
  %>
          <tr>
            <td><input type="checkbox" name="id" value="<%= id %>"></td>
            <td><%= rset.getString("author") %></td>
            <td><%= rset.getString("title") %></td>
            <td>$<%= rset.getInt("price") %></td>
            <td><%= rset.getInt("qty") %></td>
          </tr>
  <%
      }
  %>
        </table>
        <br>
        <input type="submit" value="Order">
        <input type="reset" value="Clear">
      </form>
      <a href="<%= request.getRequestURI() %>"><h3>Back</h3></a>
  <%
      rset.close();
      stmt.close();
      conn.close();
    }
  %>
</body>
</html>

JSP Model 2

Music Without Borders
  • Understanding JavaServer Pages Model 2 architecture - an article by Govind Seshadri in JavaWorld in 1999
  • Coined the term MVC
  • Used a Vector for a model and put all logic for manipulating the data in it in the Controller
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import shopping.CD;
public class ShoppingServlet extends HttpServlet {
  public void init(ServletConfig conf) throws ServletException  {
    super.init(conf);
  }
  public void doPost (HttpServletRequest req, HttpServletResponse res)
      throws ServletException, IOException {
    HttpSession session = req.getSession(false);
    if (session == null) {
      res.sendRedirect("http://localhost:8080/error.html");
    }
    Vector buylist=
      (Vector)session.getValue("shopping.shoppingcart");
    String action = req.getParameter("action");
    if (!action.equals("CHECKOUT")) {
      if (action.equals("DELETE")) {
        String del = req.getParameter("delindex");
        int d = (new Integer(del)).intValue();
        buylist.removeElementAt(d);
      } else if (action.equals("ADD")) {
        //any previous buys of same cd?
        boolean match=false;
        CD aCD = getCD(req);
        if (buylist==null) {
          //add first cd to the cart
          buylist = new Vector(); //first order
          buylist.addElement(aCD);
        } else { // not first buy
          for (int i=0; i< buylist.size(); i++) {
            CD cd = (CD) buylist.elementAt(i);
            if (cd.getAlbum().equals(aCD.getAlbum())) {
              cd.setQuantity(cd.getQuantity()+aCD.getQuantity());
              buylist.setElementAt(cd,i);
              match = true;
            } //end of if name matches
          } // end of for
          if (!match) 
            buylist.addElement(aCD);
        }
      }
      session.putValue("shopping.shoppingcart", buylist);
      String url="/jsp/shopping/EShop.jsp";
      ServletContext sc = getServletContext();
      RequestDispatcher rd = sc.getRequestDispatcher(url);
      rd.forward(req, res);
    } else if (action.equals("CHECKOUT"))  {
      float total =0;
      for (int i=0; i< buylist.size();i++) {
        CD anOrder = (CD) buylist.elementAt(i);
        float price= anOrder.getPrice();
        int qty = anOrder.getQuantity();
        total += (price * qty);
      }
      total += 0.005;
      String amount = new Float(total).toString();
      int n = amount.indexOf('.');
      amount = amount.substring(0,n+3);
      req.setAttribute("amount",amount);
      String url="/jsp/shopping/Checkout.jsp";
      ServletContext sc = getServletContext();
      RequestDispatcher rd = sc.getRequestDispatcher(url);
      rd.forward(req,res);
    }
  }
  private CD getCD(HttpServletRequest req) {
    //imagine if all this was in a scriptlet...ugly, eh?
    String myCd = req.getParameter("CD");
    String qty = req.getParameter("qty");
    StringTokenizer t = new StringTokenizer(myCd,"|");
    String album= t.nextToken();
    String artist = t.nextToken();
    String country = t.nextToken();
    String price = t.nextToken();
    price = price.replace('$',' ').trim();
    CD cd = new CD();
    cd.setAlbum(album);
    cd.setArtist(artist);
    cd.setCountry(country);
    cd.setPrice((new Float(price)).floatValue());
    cd.setQuantity((new Integer(qty)).intValue());
    return cd;
  }
}

Struts and MVC2

Controller Problems

  • Poor separation of concerns
  • Tied to the environment it runs on
  • Does a lot of things...
  • ... has a lot of dependencies...
  • ... becomes really fat...
  • ... and hard to maintain.
  • And they even set output headers!

Action-Domain-Responder

  • Coined by Paul M. Jones
  • Designed to better fit client-server environments

Action

  • A function or a class implementing the Command pattern
  • Collects data from the request and passes it to Domain
  • Invoke Domain and retains the result
  • Invokes Responder with any data needed (domain data, request data, other)
    • Does not set output headers
    • Does not touch output in any way

Domain

An entry point into whatever does the domain work (Transaction Script, Service Layer, Application Service, etc.).

Responder

Deals with all output:

  • Sets cookies
  • Sets headers
  • Applies compression
  • Chooses presentation format
  • Renders templates



$app->get('/tickets', function (Request $request, Response $response) {
    $this->logger->addInfo("Ticket list");
    $tickets = $this->ticket_service->getTickets();
    return $this->ticket_responder->index(
        $response,
        ["tickets" => $tickets, "router" => $this->router]
    );
});

$app->get('/ticket/new', function (Request $request, Response $response) {
    $components = $this->ticket_service->getComponents();
    return $this->ticket_responder->add(
        $response,
        ["components" => $components]
    );
});

$app->post('/ticket/new', function (Request $request, Response $response) {
    $data = $request->getParsedBody();
    $this->ticket_service->createTicket($data);
    return $this->ticket_responder->create($response);
});

$app->get('/ticket/{id}', function (Request $request, Response $response, $args) {
    $ticket = $this->ticket_service->getTicketById($args['id']);
    return $this->ticket_responder->detail(
        $response,
        ["ticket" => $ticket]
    );
})->setName('ticket-detail');

ADR Resources

http://pmjones.io/adr/

Thank you!

Milko Kosturkov

@mkosturkov

linkedin.com/in/milko-kosturkov

mailto: mkosturkov@gmail.com

 

These slides:

https://slides.com/milkokosturkov/mvc-history-misconceptions-future/

 

Made with Slides.com