Upload
others
View
9
Download
0
Embed Size (px)
Citation preview
MVCWhat follows?
Daniel Grawunder / Christian Mierich
MVC-Frameworks
Laravel
Trygve Reenskaug
“Model–view–controller (MVC) is a software architectural pattern for implementing user interfaces.“
wikipedia
MODEL
CONTROLLERVIEW
USER
updates manipulates
usessees
Original-MVC
vs
Design Pattern
Architectural Pattern
Web-MVC
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
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
<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
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
Web-MVC
CONTROLLERIssuesController
VIEWHTML-Template
MODELIssue
HTTP-Request
Bussiness logic Persistence logic (Queries)
UI logic
No Separation of Presentation
No Architecture
No Structure
OpenStreetMap
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
Architecture The Lost YearsRobert C. Martin (Uncle Bob)
https://www.youtube.com/watch?v=WpkDN78P884#t=432
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
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
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
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
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
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
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
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
Domain
The “Rails” Way
Dat
a La
yer
User
Delivery M
echanism
View
ControllerModel
Model
Model
Model
Model
Necessary Validation
Rails propagatedValidation
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
Our World with Web-MVC
Thinking in Layers
PRESENTATION
DOMAIN
DATA
Layers of a standard application
Delivery Mechanism
CONTROLLER
HTTP-Request
HTTP - Endpoint
VIEW MODEL
DOMAINDATA
PRESENTATION
Web-MVC
Delivery Mechanism
CONTROLLER
HTTP-Request
HTTP - Endpoint
VIEW MODEL
DOMAIN
QUERIES
DATA
Thinking in 3 Layers
PRESENTATION
Delivery Mechanism
CONTROLLER
HTTP-Request
HTTP - Endpoint
USE CASE
DOMAIN DATA
Clear Domain Boundaries
DOMAIN OBJECT
QUERIES
RECORD
PRESENTATION
VIEW
Delivery Mechanism
CONTROLLER
HTTP-Request
HTTP - Endpoint
USE CASE
DOMAIN DATA
Clear Domain Boundaries
DOMAIN OBJECT
QUERIES
RECORD
DOMAINOBJECTVIEW
PRESENTATION
Delivery Mechanism
CONTROLLER
HTTP-Request
HTTP - Endpoint
USE CASE
DOMAIN DATA
Using Presenters
DOMAIN OBJECT
QUERIES
RECORD
DOMAINOBJECT
PRESEN-TERVIEW
MVP/MVVM
PRESENTATION
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
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; }}
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
Conclusion
Don’t repeat the same mistakes
Client-Side MVC
“Current JavaScript solutions suffer from "Double MVC". You need both server and client-side MVC stacks.”
David Heinemeier Hansson
Delivery Mechanism
CONTROLLER
HTTP-Request
HTTP - Endpoint
USE CASE
DOMAIN DATA
Server-Side
DOMAIN OBJECT
QUERIES
RECORD
DOMAINOBJECT
PRESEN-TERVIEW
PRESENTATION
Delivery Mechanism
CONTROLLER
HTTP-Request
HTTP - Endpoint
USE CASE
DOMAIN DATA
DOMAIN OBJECT
QUERIES
RECORD
DOMAINOBJECTSERIALIZER
PRESENTATION
Server-Side
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
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
CONTROLLERextends Ember.
Controller
Ember.js 2.0
COMPONENTextends Ember.
Component
MODELextends
DS.Model
TEMPLATEHANDLEBAR
(Presenter)
(VIEW)
(Controller)
MV-C/P
[email protected]@objectfab.de