Architecting Your Models


Published on

Provides some approaches to the "M" in MVC -- Models -- and how to get away from tying models to your database, and instead adopt sane approaches that provide more flexibility and forward exapandibility in your code.

Published in: Technology
  • nice :)
    Are you sure you want to  Yes  No
    Your message goes here
  • Hi,

    @gabreanuandrei, i'd like to add that what is so called 'domain logic' isn't limited to add behaviour inside your model classes representing the entities of the system. The business logic can live inside any class, and a service is not an exception. I think a 'well' (that's subjective) designed model layer can mix several approaches. If you take a look at Domain Driven Design for instance, you'll see that it introduces two different kind of services, one specific to the model, responsible of business operations, and being the glue between your models, and the second one dedicated to applicative code, but maybe that's the only aspect that you'll take from DDD, and you'll consolidate your approach being inspirated by other approaches radically different, and in the end, your model will be well organized, but also unique, unique for your project and unique for the business it is designed for, just like matthew pointed it out.

    I think that just opening a book and implementing your stuff exactly like it is 'right' is not necesseraly a good thing.

    At this time, i think that Matthew tried to have the best 'generic' approach he could have with the tools he had ;-)

    Today, it's not really a problem anymore since frameworks let your mind do the job !
    Are you sure you want to  Yes  No
    Your message goes here
  • @gabreanuandrei I want to point out a few things. First, this tutorial is almost 3 years old at this point. When I originally gave it, domain modelling in PHP was a fairly novel concept, and any information that moved developers away from having models directly extend objects that handled persistence (*ahem*ActiveRecord*ahem*) was a good step forward.

    Second: there are many, many valid ways to perform domain modelling. There is NOT a single correct way, however. Your choices will be informed by the project -- how big or small it is, how many developers you have, the complexity of the domain.

    Is the advice in the presentation _wrong_? I'd likely write a quite different presentation today -- but I wouldn't necessarily call the information in it inaccurate or bad. And yes, I've got a copy of Fowler's POEAA right next to me on my shelf, and have had my copy for many years. It's a guidebook, not a rulebook. ;-)
    Are you sure you want to  Yes  No
    Your message goes here
  • Isn't this tutorial actually bad? Based on Matin Fowler's article (and more on the web if you google) - basically the Service should NOT contain logic at all rather than just guide the client into using the Domain Model properly but also provide a public API of your application to clients (controllers, API's, cli scripts, etc.)

    What do you think?
    Are you sure you want to  Yes  No
    Your message goes here
  • I'm curious as to how you'd separate the models from persistence when using an ORM like Doctrine.

    Doctrine seems to be engineered from the perspective that you'd be using the Doctrine_Record extending models directly.

    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Architecting Your Models

  1. Architecting Your Models Matthew Weier O'Phinney Project Lead Zend Framework
  2. Goals
  3. <ul><li>Learn to recognize why old habits may be bad
  4. Learn several design patterns that can help you write testable, maintainable code
  5. Learn some techniques for altering existing behavior without rewriting existing code </li></ul>
  6. What We've Learned and What We Do
  7. Oooh! Let's create the schema!
  8. Write code that uses the DB
  9. <ul><li>Plain Old Mysql (POM)
  10. ActiveRecord
  11. Table/Row Data Gateway </li></ul>
  12. And then …
  13. <ul><li>We start lamenting about performance.
  14. We end up refactoring for every new feature (e.g. caching, logging).
  15. We need to go to a Service Oriented Architecture, and effectively refactor twice. </li></ul>
  17. Step One
  18. Models are just classes. Create classes.
  19. Models have metadata and behavior ; create properties and methods .
  20. class Person implements PersonInterface { // Metadata protected $_email ; protected $_password ; protected $_username ; // Cheat: use overloading to // provide setters/getters public function __get( $name ) { } public function __set( $name , $value ) { } // Behaviors public function authenticate() { } public function logout() { } public function ban() { } }
  21. Step Two
  22. Now start thinking about data persistence .
  23. <ul><li>Identify what data you need to persist
  24. Identify how you'll persist the data
  25. Write code for persisting data (Data Access Layer) </li></ul>
  26. CREATE TABLE person( username VARCHAR PRIMARY KEY , password VARCHAR , email VARCHAR , ); class PersonTable extends Zend_Db_Table_Abstract { protected $_name = 'person' ; protected $_primary = 'username' ; }
  27. Step Three
  28. Map your model to your data.
  29. Common approaches <ul><li>Transaction Script (can even use POM)
  30. Table Module (often with ActiveRecord or Table Data Gateway)
  31. Data Mapper / ORM </li></ul>
  32. class PersonMapper implements PersonMapperInterface { public function save(PersonInterface $person ) { $data = array ( 'username' => $person ->username, 'password' => $person ->password, 'email' => $person ->email, ); $this ->getTable()->save( $data ); } public function fetch( $username ); public function getTable(); public function setTable( $table ); }
  33. Some notes: <ul><li>Data !== Relational Database .
  34. Data could come from a document database, filesystem, cache, or web service.
  35. Choose an ORM that allows you to generate your schema from your entities; allows you to easily model first, and persistence comes for free. </li></ul>
  36. Step Four
  37. Move business and application logic to a Service Layer .
  38. Applications are like onions; they have layers. Photo © 2008, Mike Chaput-Branson
  39. The Service Layer provides application logic on top of your models
  40. Service Layer in perspective Data Access Objects and Data store(s) Data Mappers Domain Models Service Layer
  41. Benefits to a Service Layer <ul><li>Allows easy consumption of the application via your MVC layer
  42. Allows easy re-use of your application via services
  43. Write CLI scripts that consume the Service Layer </li></ul>
  44. What kind of application logic? <ul><li>Validation and filtering
  45. Authentication and Authorization
  46. Transactions and interactions between model entities </li></ul>
  47. class PersonService { public function create( array $data ) { $person = new Person(); if (! $data = $this ->getValidator() ->isValid( $data ) ) { throw new InvalidArgumentException(); } $person ->username = $data [ 'username' ]; $person ->password = $data [ 'password' ]; $person ->email = $data [ 'email' ]; $this ->getMapper()->save( $person ); return $person ; } }
  48. Decorating for fun and profit
  49. Decorators allow you to add or alter functionality of an existing class.
  50. Typical decorators … <ul><li>Implement the same interface(s) of the class being decorated
  51. Often use overloading to proxy to the decorated class
  52. Override specific behavior(s) you wish to modify or enhance
  53. Add new behaviors that use existing behaviors in the decorated class </li></ul>
  54. Refactor to add caching? No! Decorate!
  55. class CachingPersonMapper implements PersonMapperInterface { public function __construct( PersonMapperInterface $mapper ) { $this ->_mapper = $mapper ; } public function fetch( $username ) { $cache = $this ->getCache(); if (! $person = $cache ->load( $username )) { $person = $this ->_mapper ->fetch( $username ); $cache ->save( $person , $username ); } return $person ; } }
  56. Refactor to provide alternate return formats? (e.g., JSON, XML, etc.) No! Decorate!
  57. class JsonPerson implements PersonInterface { public function __construct( PersonInterface $person ) { $this ->_person = $person ; } public function __toString() { $data = array ( 'username' => $this ->_person->username, 'email' => $this ->_person->email, ); return json_encode( $data ); } }
  58. Nicely Formed Objects
  59. <ul><li>Rebuilding and refactoring is costly and painful
  60. Good OOP and encapsulation CAN make your life easier
  61. Testing is easier than debugging
  62. Choose a good ORM to expedite development. (Doctrine, Object Freezer, Zend_Entity, etc.) </li></ul>
  63. Think beyond the DB!
  64. Thank you! <ul><li>Feedback:
  65. Twitter: @weierophinney
  66. Blog: </li></ul>