State Machines to State of the Art


Published on

Smart efficient design using REST and MVC.

Web applications are everywhere now, but many of them misuse the basic concepts laid down by the HTTP protocol, miss the benefits of making the application and the API the same thing, and don't set themselves up to grow if things take off.

This talk will look at the design decisions you need to make to ensure that your application really is ReST-ful, how we fit that cleanly into MVC, and how state machines can help us manage clean state changes in a stateless protocol. The talk will go into some of the available design patterns with class diagrams and code snippets showing how and where to implement them.

Originally presented at PHP UK 2009.

1 Comment
  • Computer Science in general solved these problems quite a while ago, that's the point. This talk illustrates some of the things web developers often forget about the protocol they use and as a result make their lives more complicated. J2EE provides implementations of these patterns, but also still provides enough leeway for developers to do it wrong.
    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
  • Today I'm going to be talking about how we go about designing web applications and some of the tools we can use to make those designs simple, robust, modular, and hopefully somewhat future-proof. A little about me, I'm a lead developer at Plusnet which is a BT-owned ISP up in Sheffield. People sometimes seem surprised that an ISP needs a dev. dept when presumably all we need to do is plug a few wires in and flick a few switches in an exchange. The difference is that we focus on automating as much of the entire process as possible: signing up, provisioning, billing, customer support and so on. We've done most of this in-house, so we need developers to make sure it keeps working and to identify new areas we can automate. The majority of this is in PHP. We've got some Java in some back-end places, and then a smattering of Perl, C, and bash scripts filling in the cracks. We've been doing this for over a decade now, so as you can imagine there's a lot of code there. That leads me onto why I wanted to do this talk...
  • State Machines to State of the Art

    1. 1. PHP UK 2009 <ul><ul><li>State Machines to State of the Art </li></ul></ul><ul><ul><li>Smart, Efficient Design using REST and MVC </li></ul></ul>Rowan Merewood, Lead Developer, Plusnet
    2. 2. Motivation <ul><ul><li>Trying to be a better Software Engineer </li></ul></ul>
    3. 3. The Tools <ul><ul><li>HTTP, REST, CRUD, MVC, </li></ul></ul><ul><ul><li>State Machines </li></ul></ul>
    4. 4. HTTP Protocol <ul><ul><li>A stateless way of interacting with resources </li></ul></ul>
    7. 7. HTTP Protocol HEAD GET POST PUT DELETE TRACE OPTIONS CONNECT Safe GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered &quot;safe&quot;.
    8. 8. HTTP Protocol HEAD GET POST PUT DELETE TRACE OPTIONS CONNECT Idempotent Methods can also have the property of &quot;idempotence&quot; in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request
    9. 9. What's Important? <ul><ul><li>User Experience </li></ul></ul><ul><ul><li>(of course!) </li></ul></ul>
    10. 10. What's Important? <ul><ul><li>Safe == Read Only </li></ul></ul><ul><ul><li>Idempotent == Predictable </li></ul></ul>
    11. 11. What went wrong? <ul><ul><li>Great Power -> Great Responsibility? </li></ul></ul>
    12. 12. What was abused? <ul><ul><li>HTTP, Sessions, and Cookies – oh my! </li></ul></ul>
    13. 13. Safe? <ul><ul><li> </li></ul></ul>
    14. 14. Stateless? <ul><ul><li>I'll just press ”Back” then... </li></ul></ul>
    15. 15. REST <ul><ul><li>Representational State Transfer </li></ul></ul>
    16. 16. REST <ul><ul><li>Nouns not verbs </li></ul></ul>
    17. 17. REST <ul><ul><li>/getOrder?id=123 vs. /order/123 </li></ul></ul><ul><ul><li>/addContact vs. /contacts/ </li></ul></ul><ul><ul><li>/updateArticle?id=abc vs. /article/abc </li></ul></ul>
    18. 18. REST <ul><ul><li>REST is ideal for implementing over HTTP </li></ul></ul>
    19. 19. Design <ul><ul><li>Enough front-end stuff, </li></ul></ul><ul><ul><li>what's going to do the work? </li></ul></ul>
    20. 20. The Problem <ul><ul><li>Booking train tickets </li></ul></ul>
    21. 21. Analysis <ul><ul><li>What's my resource? </li></ul></ul><ul><ul><li>Tickets </li></ul></ul>Origin, Destination, Departs, Class, Price
    22. 22. CRUD <ul><ul><li>Create, Read, Update, Delete </li></ul></ul>
    23. 23. CRUD Create Delete Update Read Exists PUT/POST DELETE PUT/POST GET
    24. 24. Analysis <ul><ul><li>Create, Read, Update, Delete, </li></ul></ul><ul><ul><li>Book, Cancel, Finalise, Refund, Archive </li></ul></ul>
    25. 25. Analysis Create Update Book Finalise Delete Cancel Refund Archive Archive Drafted Refunded Cancelled Deleted Finalised Booked Archived
    26. 26. Code Rowan On Rails (I am so, so sorry!)
    27. 27. URLs <ul><ul><li>/tickets </li></ul></ul><ul><ul><li>GET : list tickets </li></ul></ul><ul><ul><li>POST : create ticket </li></ul></ul><ul><ul><li>/ticket </li></ul></ul><ul><ul><li>GET : display ticket form </li></ul></ul><ul><ul><li>/ticket/123 </li></ul></ul><ul><ul><li>GET : display ticket </li></ul></ul><ul><ul><li>/ticket/123/booked </li></ul></ul><ul><ul><li>POST : state transition </li></ul></ul>
    28. 28. Controllers
    29. 29. Routing <ul><ul><li>$ router = $ frontController ->getRouter(); </li></ul></ul><ul><ul><li>$ route = new Zend_Controller_Router_Route ( </li></ul></ul><ul><ul><li>' ticket/ :ticketId ', </li></ul></ul><ul><ul><li>array( </li></ul></ul><ul><ul><li>' controller ' => ' ticket ', </li></ul></ul><ul><ul><li>' action ' => ' fetch ', </li></ul></ul><ul><ul><li>) </li></ul></ul><ul><ul><li>); </li></ul></ul><ul><ul><li>$ router ->addRoute(' ticketFetch ', $ route ); </li></ul></ul><ul><ul><li>$ route = new Zend_Controller_Router_Route ( </li></ul></ul><ul><ul><li>' ticket/ :ticketId / :stateHandle ', </li></ul></ul><ul><ul><li>array( </li></ul></ul><ul><ul><li>' controller ' => ' ticket ', </li></ul></ul><ul><ul><li>' action ' => ' changestate ', </li></ul></ul><ul><ul><li>) </li></ul></ul><ul><ul><li>); </li></ul></ul><ul><ul><li>$ router ->addRoute(' ticketChangestate ', $ route ); </li></ul></ul><ul><ul><li>$ frontController ->setRouter($ router ); </li></ul></ul>
    30. 30. Actions <ul><ul><li>public function fetchAction () </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>$ model = $ this ->_getModel(); </li></ul></ul><ul><ul><li>$ ticket = $ model ->fetchTicket($ this ->_getParam(' ticketId ')); </li></ul></ul><ul><ul><li>$ this ->view->ticket = $ ticket ; </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>public function changestateAction () </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>$ model = $ this ->_getModel(); </li></ul></ul><ul><ul><li>$ ticket = $ model ->fetchTicket($ this ->_getParam(' ticketId ')); </li></ul></ul><ul><ul><li>if ($ this ->getRequest()->isPost()) { </li></ul></ul><ul><ul><li>$ model -> setState ($ this ->_getParam(' stateHandle ')); </li></ul></ul><ul><ul><li>$ model -> save (); </li></ul></ul><ul><ul><li>$ redirector = $ this ->_helper->getHelper(' Redirector '); </li></ul></ul><ul><ul><li>$ redirector ->gotoRoute( </li></ul></ul><ul><ul><li>array(' ticketId ' => $ this ->_getParam(' ticketId ')), </li></ul></ul><ul><ul><li>' ticketFetch '); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>} </li></ul></ul>
    31. 31. Model
    32. 32. Model
    33. 33. Model <ul><ul><li>public function update (Model_TicketData $ data ) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>$ this ->setState(' Draft '); </li></ul></ul><ul><ul><li>$ this ->_data = $ data ; </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>public function setState ($ handle ) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>$ classname = ' Model_ '.$ handle .' TicketState '; </li></ul></ul><ul><ul><li>$ state = new $ classname ($ this ); </li></ul></ul><ul><ul><li>$ this ->_state = $ state ; </li></ul></ul><ul><ul><li>} </li></ul></ul>
    34. 34. States
    35. 35. States <ul><ul><li>class Model_DraftedTicketState extends Model_TicketState_Abstract </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>public function __construct (Model_Ticket $ ticket ) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>$ curState = $ ticket ->getState(); </li></ul></ul><ul><ul><li>if (!$ this ->isValidPrevState($ curState )) { </li></ul></ul><ul><ul><li>throw new Model_InvalidPrevStateException( </li></ul></ul><ul><ul><li>' Tried to move to &quot;Drafted&quot; state from '.$ curState ); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>private function isValidPrevState (Model_TicketState_Abstract $ state ) </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>if ( </li></ul></ul><ul><ul><li>is_null($ state ) || </li></ul></ul><ul><ul><li>$ state instanceof Model_DraftedTicketState || </li></ul></ul><ul><ul><li>$ state instanceof Model_BookedTicketState </li></ul></ul><ul><ul><li>) { </li></ul></ul><ul><ul><li>return true; </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>return false; </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>} </li></ul></ul>
    36. 36. Benefits <ul><ul><li>Business logic solely in </li></ul></ul><ul><ul><li>Model_*TicketState classes </li></ul></ul>
    37. 37. Benefits <ul><ul><li>Easy to add new states </li></ul></ul><ul><ul><li>No change to MVC classes </li></ul></ul>
    38. 38. Benefits <ul><ul><li>Decoupled </li></ul></ul><ul><ul><li>I'm tempted to go hook this up to Zend_Rest </li></ul></ul>
    39. 39. Cons <ul><ul><li>Lots of classes </li></ul></ul>
    40. 40. Cons <ul><ul><li>Method-calling overhead </li></ul></ul>
    41. 41. Cons <ul><ul><li>Potential to over-engineer! </li></ul></ul>
    42. 42. Questions <ul><ul><li>And possibly answers... </li></ul></ul>