48
MVC What follows? Daniel Grawunder / Christian Mierich

What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

  • Upload
    others

  • View
    9

  • Download
    0

Embed Size (px)

Citation preview

Page 1: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

MVCWhat follows?

Daniel Grawunder / Christian Mierich

Page 2: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

MVC-Frameworks

Laravel

Page 3: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Trygve Reenskaug

Page 4: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

“Model–view–controller (MVC) is a software architectural pattern for implementing user interfaces.“

wikipedia

Page 5: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

MODEL

CONTROLLERVIEW

USER

updates manipulates

usessees

Page 6: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Original-MVC

vs

Design Pattern

Architectural Pattern

Web-MVC

Page 7: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Example App Issue-Tracker

$ rails generate scaffold Issue title:string description:string state:integer

class IssuesController < ApplicationController

def index @issues = Issue.all end

def new @issue = Issue.new end

def show @issue = Issue.find(params[:id]) end

...

end

<h1>Listing Issues</h1><table> <thead> <tr> <td>Title</td> <td>Description</td> <td>State</td> </tr> </thead> <tbody> <% @issues.each do |issue| %> <tr> <td><%= issue.title %></td> <td><%= issue.description %></td> <td><%= issue.state %></td> </tr> <% end %> <tbody><table>

class Issue < ActiveRecord::Baseend

View Controller Model

creates

Page 8: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

def index retrieve_query sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria) sort_update(@query.sortable_columns) @query.sort_criteria = sort_criteria.to_a

if @query.valid? case params[:format] when 'csv', 'pdf' @limit = Setting.issues_export_limit.to_i if params[:columns] == 'all' @query.column_names = @query.available_inline_columns.map(&:name) end when 'atom' @limit = Setting.feeds_limit.to_i when 'xml', 'json' @offset, @limit = api_offset_and_limit @query.column_names = %w(author) else @limit = per_page_option end

@issue_count = @query.issue_count @issue_pages = Paginator.new @issue_count, @limit, params['page'] @offset ||= @issue_pages.offset @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version], :order => sort_clause, :offset => @offset, :limit => @limit) @issue_count_by_group = @query.issue_count_by_group

...

Redmine Issues-Controller

Redmine - app/controllers/issues_controllers.rb

Page 9: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

<div class="contextual"><% if [email protected]_record? && @query.editable_by?(User.current) %> <%= link_to l(:button_edit), edit_query_path(@query), :class => 'icon icon-edit' %><% end %></div>

<h2><%= @query.new_record? ? l(:label_issue_plural) : @query.name %></h2><% html_title(@query.new_record? ? l(:label_issue_plural) : @query.name) %><%= form_tag({ :controller => 'issues', :action => 'index', :project_id => @project }, :method => :get, :id => 'query_form') do %> <div id="query_form_with_buttons" class="hide-when-print"> <%= hidden_field_tag 'set_filter', '1' %> <div id="query_form_content"> ... </div> <p class="buttons">

... <% if @query.new_record? && User.current.allowed_to?(:save_queries, @project, :global => true) %> <%= link_to_function l(:button_save), "$('#query_form').attr('action', '#{ @project ? new_project_query_path(@project) : :class => 'icon icon-save' %> <% end %> </p> </div><% end %><%= error_messages_for 'query' %><% if @query.valid? %> <% if @issues.empty? %> <p class="nodata"><%= l(:label_no_data) %></p> <% else %> <%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %>

Redmine Issue-Index-View

Redmine - app/view/issues/index.html.erb

Page 10: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

class Issue < ActiveRecord::Base validates_length_of :subject, :maximum => 255 validates_inclusion_of :done_ratio, :in => 0..100 validates :estimated_hours, :numericality => {:greater_than_or_equal_to => 0, :allow_nil => true, :message => :invalid} validates :start_date, :date => true validates :due_date, :date => true validate :validate_issue, :validate_required_fields before_validation :clear_disabled_fields before_save :close_duplicates, :update_done_ratio_from_issue_status, :force_updated_on_change, :update_closed_on, :set_assigned_to_was

scope :recently_updated, lambda { order("#{Issue.table_name}.updated_on DESC") }

# Returns true if user or current user is allowed to view the issue def visible?(usr=nil) (usr || User.current).allowed_to?(:view_issues, self.project) do |role, user| if user.logged? case role.issues_visibility when 'all' true when 'default' !self.is_private? || (self.author == user || user.is_or_belongs_to?(assigned_to)) when 'own' self.author == user || user.is_or_belongs_to?(assigned_to) else false end else !self.is_private?

...

Redmine Issue-Model

Redmine - app/models/issues.rb

Page 11: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Web-MVC

CONTROLLERIssuesController

VIEWHTML-Template

MODELIssue

HTTP-Request

Bussiness logic Persistence logic (Queries)

UI logic

Page 12: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

No Separation of Presentation

Page 13: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

No Architecture

Page 14: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

No Structure

Page 15: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

OpenStreetMap

Page 16: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

The heart of software is its ability to solve domain-related problems

for its user.All other features, vital though they may be, support this

basic purpose.Eric Evans, Domain-Driven DESIGN

Page 17: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Architecture The Lost YearsRobert C. Martin (Uncle Bob)

https://www.youtube.com/watch?v=WpkDN78P884#t=432

Page 18: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

User

Presenter

View Model

View

Delivery M

echanism

Controller

(Use Case)Interactor

DomainEntity

Dat

a La

yer

Entity

Entity

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

< I >

Boundary

< I >

Boundary

Clean Architecture

Page 19: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

User

Presenter

View Model

View

Delivery M

echanism

Controller

(Use Case)Interactor

DomainEntity

Entity

Entity

< I >

Boundary

< I >

Boundary

User

Presenter

View Model

View

Delivery M

echanism

Controller

Clean Architecture

< I >

Boundary

< I >

Boundary

Entity

Entity

Entity

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

Page 20: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

User

Presenter

View Model

View

Delivery M

echanism

Controller

(Use Case)Interactor

DomainEntity

Entity

Entity

< I >

Boundary

< I >

Boundary

User

Presenter

View Model

View

Delivery M

echanism

Controller

Clean Architecture

< I >

Boundary

< I >

Boundary

Entity

Entity

Entity

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

Request Model

Page 21: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

User

Presenter

View Model

View

Delivery M

echanism

Controller

(Use Case)Interactor

DomainEntity

Entity

Entity

< I >

Boundary

< I >

Boundary

User

Presenter

View Model

View

Delivery M

echanism

Controller

Clean Architecture

< I >

Boundary

< I >

Boundary

Entity

Entity

Entity

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

Request Model

Page 22: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

User

Presenter

View Model

View

Delivery M

echanism

Controller

(Use Case)Interactor

DomainEntity

Entity

Entity

< I >

Boundary

< I >

Boundary

User

Presenter

View Model

View

Delivery M

echanism

Controller

Clean Architecture

< I >

Boundary

< I >

Boundary

Entity

Entity

Entity

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

Request Model

Page 23: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

User

Presenter

View Model

View

Delivery M

echanism

Controller

(Use Case)

DomainEntity

Entity

Entity

< I >

Boundary

< I >

Boundary

User

Presenter

View Model

View

Delivery M

echanism

Controller

Clean Architecture

< I >

Boundary

< I >

Boundary

Entity

Entity

Entity

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPIInteractor

Result Model

Page 24: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

User

Presenter

View Model

View

Delivery M

echanism

Controller

(Use Case)

DomainEntity

Entity

Entity

< I >

Boundary

< I >

Boundary

User

Presenter

View Model

View

Delivery M

echanism

Controller

Clean Architecture

< I >

Boundary

< I >

Boundary

Entity

Entity

Entity

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

Dat

a La

yer

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPIInteractor

Result Model

Page 25: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

User

Presenter

View Model

View

Delivery M

echanism

Controller

(Use Case)Interactor

DomainEntity

Dat

a La

yer

Entity

Entity

EntityGateway

ImplementationEntity

Gateway

< I >

DatabaseAPI

< I >

Boundary

< I >

Boundary

Clean Architecture

A good architecture allows major decisions to be deferred

Robert C. Martin, Architecture the Lost Years

Page 26: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Domain

The “Rails” Way

Dat

a La

yer

User

Delivery M

echanism

View

ControllerModel

Model

Model

Model

Model

Necessary Validation

Rails propagatedValidation

Page 27: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Domain

The “Rails” Way

Dat

a La

yer

User

Delivery M

echanism

View

ControllerModel

Model

Model

Model

Model

Necessary Validation

Rails propagatedValidation

Rigidity

Fragility

Immobility

Page 28: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Our World with Web-MVC

Page 29: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Thinking in Layers

Page 30: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

PRESENTATION

DOMAIN

DATA

Layers of a standard application

Page 31: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Delivery Mechanism

CONTROLLER

HTTP-Request

HTTP - Endpoint

VIEW MODEL

DOMAINDATA

PRESENTATION

Web-MVC

Page 32: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Delivery Mechanism

CONTROLLER

HTTP-Request

HTTP - Endpoint

VIEW MODEL

DOMAIN

QUERIES

DATA

Thinking in 3 Layers

PRESENTATION

Page 33: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Delivery Mechanism

CONTROLLER

HTTP-Request

HTTP - Endpoint

USE CASE

DOMAIN DATA

Clear Domain Boundaries

DOMAIN OBJECT

QUERIES

RECORD

PRESENTATION

VIEW

Page 34: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Delivery Mechanism

CONTROLLER

HTTP-Request

HTTP - Endpoint

USE CASE

DOMAIN DATA

Clear Domain Boundaries

DOMAIN OBJECT

QUERIES

RECORD

DOMAINOBJECTVIEW

PRESENTATION

Page 35: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Delivery Mechanism

CONTROLLER

HTTP-Request

HTTP - Endpoint

USE CASE

DOMAIN DATA

Using Presenters

DOMAIN OBJECT

QUERIES

RECORD

DOMAINOBJECT

PRESEN-TERVIEW

MVP/MVVM

PRESENTATION

Page 36: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Using Presenters

class IssuePresenter < DelegateClass(Issue)

def initialize issue, current_user super(issue) @issue = issue @current_user = current_user end

def show_edit_link? current_user.admin? || issue.user_id == current_user.id end

end

Page 37: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Presenters in Spring MVC

@Controller@RequestMappingpublic class IssuesController { @Autowired private IssueService issueService; @RequestMapping(method=RequestMethod.GET, value="/") public ModelAndView showAllIssues() { List<Issue> issues = issueService.findAll(); ModelAndView modelAndView = new ModelAndView("issues"); modelAndView.addObject("issues", issues);

return modelAndView; }}

Page 38: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Delivery Mechanism

CONTROLLER

HTTP-Request

HTTP - Endpoint

USE CASE

DOMAIN DATA

Web-App is not very special

DOMAIN OBJECT

QUERIES

RECORD

DOMAINOBJECT

PRESEN-TERVIEW

MVP/MVVM

PRESENTATION

Page 39: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Conclusion

Page 40: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Don’t repeat the same mistakes

Page 41: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Client-Side MVC

Page 42: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

“Current JavaScript solutions suffer from "Double MVC". You need both server and client-side MVC stacks.”

David Heinemeier Hansson

Page 43: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Delivery Mechanism

CONTROLLER

HTTP-Request

HTTP - Endpoint

USE CASE

DOMAIN DATA

Server-Side

DOMAIN OBJECT

QUERIES

RECORD

DOMAINOBJECT

PRESEN-TERVIEW

PRESENTATION

Page 44: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Delivery Mechanism

CONTROLLER

HTTP-Request

HTTP - Endpoint

USE CASE

DOMAIN DATA

DOMAIN OBJECT

QUERIES

RECORD

DOMAINOBJECTSERIALIZER

PRESENTATION

Server-Side

Page 45: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

Backbone.js

VIEWextends Backbone.

View

MODELextends Backbone.

Model

var MyView = Backbone.View.extends(...)var MyModel = Backbone.Model.extend(...)

var view = new MyView({model: new MyModel()});

(Controller)MV

Page 46: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

PRESENTERextends Backbone.

Model

Backbone.js

VIEWextends Backbone.

View

MODELextends Backbone.

Model

var MyView = Backbone.View.extends(...)var MyModel = Backbone.Model.extend(...)var MyPresenter = Backbone.Model.extend(...)

var presenter = new MyPresenter({model: new MyModel()})var view = new MyView({model: presenter});

(Controller)MVP

Page 47: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

CONTROLLERextends Ember.

Controller

Ember.js 2.0

COMPONENTextends Ember.

Component

MODELextends

DS.Model

TEMPLATEHANDLEBAR

(Presenter)

(VIEW)

(Controller)

MV-C/P

Page 48: What follows? - JUG Saxony Day 2018...Example App Issue-Tracker $ rails generate scaffold Issue title:string description:string state:integer class IssuesController < ApplicationController

[email protected]@objectfab.de