| 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 |