blob: dd5567e86e6f2a9c09d03172e79212a2693f53f1 [file] [log] [blame]
eZ Component: MvcTools, Requirements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Author: James Pic, Tobias Schlitt
:Revision: $Revision$
:Date: $Date$
Target and scope
================
The scope of this document is to describe the requirements for a component
providing classes to implement a MVC_ architecture for a web application.
This document does not describe the design of the named component, but only
summarizes the requirements for it as discussed on the developer mailing list.
Where suitable, design related topics will be touched, but a dedicated design
document is available, defining APIs, classes and the architectural structure
of the component.
Note that if you don't understand a term, you should have a look at the
section `Clarification of terms`_.
Introduction
============
Model-View-Controller (MVC) is a common architecture pattern to implement all
kinds of applications using the object-oriented design paradigm. The goal of
the MvcTools component is to give users a basis for such architectures for
web applications written in PHP 5. The component shall not provide a full-
featured implementation of the MVC architecture, but should help users to
easily create their own, custom implementations.
A good starting point to learn more about MVC is the Wikipedia article:
http://en.wikipedia.org/wiki/Model-view-controller
The design document contains an example dispatcher that shows the control
flow. A few diagrams are also available containing a high level overview of
the different design parts.
Component integration
=====================
eZ Components already provide some components that are useful when
implementing an MVC. However, one basic goal of eZ Components is to keep
each component as independent as possible and to realize dependencies
through so-called tie-in components. Therefore the mechanisms realized in an
MVC component should be that far abstracted, that other components can be
tied in and provide only very basic implementations on its own. This also
allows users to implement their own mechanisms. The following components
have been identified as possible tie-ins:
- EventLog_ (error-handling)
- Mail_ (error-handling, view)
- PersistentObject_ (models)
- Template_ (view)
- Tree_ (routing)
- Url_ (routing)
- UserInput_ (routing)
.. _UserInput: http://ezcomponents.org/docs/tutorials/UserInput
.. _Url: http://ezcomponents.org/docs/tutorials/Url
.. _PersistentObject: http://ezcomponents.org/docs/tutorials/PersistentObject
.. _EventLog: http://ezcomponents.org/docs/tutorials/EventLog
.. _Mail: http://ezcomponents.org/docs/tutorials/Mail
.. _Tree: http://ezcomponents.org/docs/tutorials/Tree
For each of these components a tie-in component could considered to be
implemented at a later stage. This should be kept in mind when designing the
classes/interfaces for the MvcTools component.
Design requirements
===================
This section summarizes the requirements regarding the later following design
document.
Layers
------
The MvcTools component should distinguish certain layers to allow users to
easily adjust and replace certain functionality. Therefore, the following
requirements have been specified.
Controllers process and respond to events, typically user actions, and may
invoke changes on the model. In a case, controllers run an action using a
single argument: an input-object, or a request object. A controller returns an
result-object after being run.
The router is responsible to select the controller to run and
the action to call on it. To create the request-object it requires the
so-called request parser. After the controller has been run, the result
object is send to the view-manager, which is responsible to select the
correct rendering mode for the output protocol/format.
The request object is responsible of generating an input object, which
contains data that is not specific to the protocol; it can be used in
protocol-independent controllers.
Those two layers use one controller per request. They handle the
client-protocol, so that the controller doesn't have to work on raw input data
(e.g. GET/POST/...) and does not have to generate any specific output format
(e.g. HTML, XML, plain text,...) or anything that is protocol-specific. The
two layers abstract I/O from the controller as described in the specific
section.
Summary
^^^^^^^
- One controller and action can be run in one request.
- Controllers should neither work on raw input directly, nor create a specific
kind of result, but should only work on abstracted request and result
objects.
- Protocol dependent controllers may access the raw data.
- It should be straight forward to test any action.
Ideas
^^^^^
A PersistentObject_ tie-in could be supplied to easily realize Crud_
controllers.
.. _PersistentObject: http://ezcomponents.org/docs/tutorials/PersistentObject
.. _Crud: http://en.wikipedia.org/wiki/Create%2C_read%2C_update_and_delete
Request Parser
--------------
Requests should be parsed by the request parser. The request parsers are
protocol dependent, and return a request object. The request object in
question contains protocol-abstracted input such as the user agent, message ID
and character set encodings. The object also will contain raw protocol data
through a different property.
The request parser is the first layer in action, it's possible to run any
controller with it's resulting request object, and protocol independent
controllers with only the protocol-abstracted data that the request object
provides. It handles input parsing and filtering, but delegates input
validation to the controller.
Summary
^^^^^^^
- The request parser is responsible to filter the incoming request and to
extract all relevant data into the request object in a protocol independent
way.
- The request object also contains raw protocol data.
Filtering
---------
Filtering is the process of modifying the ezcMvcRequest, ezcMvcResult and
ezcMvcResponse objects. Filtering in the ezcMvcRequest object can for example
re-encode all the different input variables in a different character set.
Filtering on the result object that comes from the controller's action could
for example convert currencies and filtering in the response could minimize
CSS, gzip content and re-encode to an output character set.
Multilevel I/O abstraction
---------------------------
Modern applications often require to deal with different input and output
protocols and formats (for example HTTP-GET, HTTP-POST, SOAP for the input
part and HTML, PDF, ReST-webservices for the output part).
Therefore, it should be possible to use abstracted input and output formats,
into dedicated objects; which are not specific to a certain protocol or
format.
Controllers will however receive all the input data, besides the abstracted
data that comes in from the request. It's up to the developer to make sure
none of the protocol-specific data is used in case he wants a
protocol-abstract controller. This allows protocol-specific controllers to
access the raw request data.
The request-object contains all the request data.
A beneficial side-effect relates to controller-testing: Creating request and
result mocks and fixtures allows straight-forward TDD.
Summary
^^^^^^^
- Some controllers should not know about the input and output environment
out work on abstract objects only.
- Protocol dependent controllers should have access to all the request data.
Routing
-------
Each request runs a controller. A controller returns an abstract value that
should be usable with any output formats and protocols, the result object.
The router selects the controller to run. There are two types of routers.
Protocol dependent routers are simply called from the dispatcher and select an
appropriate controller. Protocol independent routers are created through a
router manager. The result of a controller action can either be a abstract
result object, or an internal redirect result. The internal redirect result
contains a new request object that can be picked up by the dispatcher and
appropriate re-running of the router should occur.
Routers must also provide a mechanism to create URLs from request-objects,
that may be used to access a certain controller and action with a certain
view-manager and defined parameters. This method must be accessible from the
controllers and the view-manager. This is required for creating back-links or
links in views with a specific base URL in mind.
Summary
^^^^^^^
Routers are responsible for:
- Selecting controllers based on the input
- Running the controller and collecting either the request object or the
abstract input object.
Routers may be asked by a controller to re-route the request to another
controller and must therefore be accessible from any action.
Tie-in components for the Url_ and Tree_ components should be provided
to realize routers. The design of these components should be considered
important while designing the MvcTools component.
.. _Tree: http://ezcomponents.org/docs/tutorials/Tree
.. _Url: http://ezcomponents.org/docs/tutorials/Url
Controller
----------
Controllers provide the actions that implement the business logic of the
application. Where a controller deals with modules (eZ Publish uses content
"content" f.e.) the action is what to do with this modules ("view" or "edit" a
part of the "content"). The controllers accept the request object as input.
Each controller implementation can provide two methods that provide filter
chains. These filter chains are run before - on the request object - and after
the action executions - on the result object to implement application specific
filtering.
It is left to the user on how the interaction with the model happens. There
are two basic options. One is where the model is directly used in the
controller through f.e. the PersistentObjectSession's singleton pattern. The
second possibility is to depend on a service interface. This service interface
then uses the PersistentSession to query, and work on the model. This service
interface architecture prevents the controller from being dependent on the
model.
Summary
^^^^^^^
- Controllers are user-implemented
- Controllers allow pre-run request filters
- Controllers allow post-run result filters
- There are two ways of dealing with a model
View-management
---------------
Controllers return a value that cannot be send directly to the client
(abstract result object). Another layer uses the return value and processes
it into a specific response. This layer is called the view-manager.
The view manager receives the result object and the request object
and can decide on the base of both of them which view handler to use.
The view handler is responsible for rendering one result object into a proper
response. Only one view-handler can be used to generate a certain response.
It receives one abstract result object. A view-handler is responsible to
render a certain output format for a certain protocol. The view-handler to use
is determined by the used view-manager, which selects a view handler similarly
to a router selecting a controller.
For example, the view-handler that generate HTML/HTTP-responses is separated
from the view-handler that generates XML/HTTP-response.
Summary
^^^^^^^
View-manager role:
- Receives the request object and the abstract result objects
- Builds the concrete response and sends it to the client
A tie-in for the Template_ component should be provided with the first
release of MvcTools.
.. _Template: http://ezcomponents.org/docs/tutorials/Url
Error-handling
--------------
During debugging it must be possible to present helpful error messages
to the developer, but on a production system no errors from the MVC
should be shown to the user, but the developer should be able to handle
them gracefully.
Some failures should be reported to the administrator in a technical manner,
allowing him to fix the problem or to handle the client's request manually.
Errors may occur during each step of the request handling, like the
following examples:
- Router cannot parse request
- Configured / requested controller could not be found
- The view can't be rendered because of incompatible data or some
template parse error
Those errors cannot be handled by the controller, because they happen
outside of it. A configurable default controller will be called for all
error messages, so the application developer may decide to send
messages, show or log the occurred error. An error during the execution
of this default controller will cause a "500 - Internal server error".
As none of the given errors is meant to be displayed to the user of the
application (but only to the developer) no translation possibilities for
the errors need to be provided.
TieIns for this default controller using the Execution, EventLog
and / or Mail component for error logging would be useful.
Summary
^^^^^^^
Actions should be able to cast an error specifying, at once:
- the verbose error message,
- the non-verbose error message or id,
- the target action to bundle the error with,
- whether actors should be notified or not.
An EventLog_ and Mail_ tie-in should be supplied by another component.
.. _EventLog: http://ezcomponents.org/docs/tutorials/EventLog
.. _Mail: http://ezcomponents.org/docs/tutorials/Mail
Testing
-------
Testing a controller is the key to quality development. Testing manually each
controller can lead to disasters: It's boring to do and therefore humans
cannot cover all controllers manually after each bug fix. The solution is
test-automation in PHP (e.g. using PHPUnit).
Controllers run with a single argument: a request object. Controllers return a
single value: An abstract result object. Running a controller with an
request-object fixture and asserting that the result matches an result-object
fixture is the procedure to test a controller.
Summary
^^^^^^^
Requested process to test a controller:
- Create a request-fixture (which can have protocol dependent values)
- Create a result-fixture (abstract output object)
- Run the controller against the request-fixture
- Assert that the controller's result equals the result-fixture
Conventions
-----------
Convention is the key for this component. Even though tie-ins will be supplied
to allow the usage of other eZ Components, all layers should be adjustable and
replaceable by the user.
The only classes that should be common to every project are the request
object, and the request and the result abstractions. All other classes should
be defined through interfaces and only very basic implementations will be
shipped with the component. Advanced implementations can be added at a later
stage, be provided via tie-ins or can be implemented by the user himself or be
installed through 3rd party code.
Shipped implementations
=======================
Routers
-------
Regexp Router
^^^^^^^^^^^^^
The Regexp Router should allow:
- creating routes with regexps,
- including routes from other application-specific routers,
- pass the regexp matches to the controller,
- allow to set any arbitary variable for a particular route, that will be
passed to the controller.
This is similar to the routing system implemented in the Django_ Python_
framework.
.. _Django: http://djangoproject.org
.. _Python: http://python.org
View handlers
-------------
MvcTemplateTiein
^^^^^^^^^^^^^^^^
The Template_ Router uses ezt_ files from the Template_ component to
render the response body.
It should allow:
- creating response bodies with several templates in an arbitrary order,
- including template view-handling configurations from other
application-specific view-handlers,
.. _ezt: http://ezcomponents.org/docs/tutorials/Template
Request Parsing
---------------
HTTP Request Parser
^^^^^^^^^^^^^^^^^^^
The HTTP Request Parser should be able to make request objects from any HTTP
request, encapsulating all possible data in a way allowing other
protocol-specific request parser to set variables the same way.
It should also provide a raw request object, with protocol specific unparsed
variables.
Special Considerations
======================
HTTP-GET should not be allowed to invoke changes on the model because this
would cause a violation of HTTP standards. HTTP GET is solely for *requesting*
data, where as HTTP PUT is meant for modifying data. (See section 8 of RFC
1945 and http://www.w3.org/2001/tag/doc/whenToUseGet.html).
We should keep testing capabilities for the extensions to this component and
the applications build upon it in mind during the design and implementation
phase.
We cannot provide the testing environment itself:
- Does not fit into the component
- Our test "component" is not an official component and can only be used to
test eZ Components themselves.
However, we could provide detailed information and possibly some helper
classes for the testing of applications build on this component.
The later application configuration layer (meaning to read configuration from
config files and configuring the parts of the MVC accordingly) should be part
of a potential "Framework" component. But this should not be part of this
document nor the MvcTools component.
In addition, this component should neither provide any automation facilities
(e.g. code generation, deployment) nor integration with components that are
not explicitly needed by it (e.g. Configuration, Authentication or Database).
Integration with such components could:
- Be build using a tie-in
- Be part of a potential "Framework" component/package stuff that might be
created in future.
Clarification of terms
======================
MVC_
Model-View-Controller (MVC) is an architectural pattern to separate data
(model) and user interface (view) concerns, so that changes to the user
interface will not affect data handling.
Model
The domain-specific representation of the information that the application
operates on. Domain logic adds meaning to raw data (e.g., calculating
whether today is the user's birthday, or the totals, taxes, and shipping
charges for shopping cart items). The PersistentObject component provides
a persistent storage mechanism (such as a database) to store data. MVC
does not specifically mention the data access layer because it is
understood to be underneath or encapsulated by the model.
View
Renders the action into a form suitable for interaction, typically a user
interface element. Multiple views can exist for a single model for
different purposes. The Template component provides a syntax-light language
for non-programmers to design the views. Therefore a tie-in with the
Template component should be provided.
Controller
Processes and responds to events, typically user actions, and may invoke
changes on the model.
Dispatcher
The dispatcher execute the different layers of MVC in order. First it
parsers the request through a request parser, then find the router through
the router manager, the controller through the router and as last the view
handler through the view manager.
Action
Controllers can provide one or several actions. Each action has a specific
process that can be called by the router. An action is an operation on a
controller. Where the controller specifies a specific resource, an action is
the operation to call on this resource. As an example the resource could be
"content" where as an action (operation) could be "view" or "edit".
Router
Routers are the first layer hit by input and the last layer that processes
the output. That is why it handles routing requests to the appropriate
action and abstracts the request/response protocol.
Fixture_
Fixtures are objects that are set to an arbitrary state for testing
purposes.
Abstraction_
It is the process or result of generalization by reducing data, typically
in order to retain only information which is usable by any router for any
protocol.
Request
The data that comes in through any protocol possible from the client to the
server. The request data consists of headers, variables, files etc and is
encapsulated in the ezcMvcRequest object.
Result
The abstract result of a controller action that can be used by the view
manager to generate a response.
Response
The data that is send from the server to the client that has been generated
by the view manager from the abstract result object.
.. _MVC: http://en.wikipedia.org/wiki/Model-view-controller
.. _Fixture: http://en.wikipedia.org/wiki/Test_fixture
.. _Abstraction: http://en.wikipedia.org/wiki/Abstraction
..
Local Variables:
mode: rst
fill-column: 78
End:
vim: et syn=rst tw=78