0
Case study: eZ PublishSymfony2 http kernel for legacy appsGaetano Giunta | PUG Milano | Maggio 2013
The drivers
 Existing codebase is 10 years old  High maintenance cost Started with no unit tests Layers and roles not properly de...
 Existing codebase is 10 years old  Widely deployed Well debugged Pitfalls have probably been uncovered by now Prove...
 Focus on our core business Experience Management Content Management NOT Framework maintenance DurableArchitecture A...
• Simple Integration with existing API• HMVC (Hierarchical Model View Controller) stack• Decoupled Components• Dependency ...
• Home brew• Zeta Components• Zend Framework 2• Symfony 2 (Full Stack!)5/18/2013gg@ez.no Slide 7Candidates
5/18/2013gg@ez.no Slide 8And the winner is…Title of presentation is a hint, really...
The challenge
Product Management SCRUM Story:«As an existing user, I don’t want to be pissed off by a new #@!$% version!»5/18/2013gg@ez....
Product Management SCRUM Story:«As an existing user, I don’t want to be pissed off by a new #@!$% version!»• 100% Data Com...
Product Management SCRUM Story:«As an existing user, I don’t want to be pissed off by a new #@!$% version!»• 100% Data Com...
A new architecture
Product Management SCRUM Story:«As an existing user, I don’t want to be pissed off by a new #@!$% version!»• 100% Data Com...
5/18/2013gg@ez.no Slide 15Dual-core architecture
Legacy version still works perfectly standalone5/18/2013gg@ez.no Slide 16BC: icing on the cake
Isn’t this what you have been waiting for?The HTTP Kernel
Request => process() => Responseuse SymfonyComponentHttpFoundationRequest;use SymfonyComponentHttpFoundationResponse;$requ...
 The HTTPKernel component “formalizes the process of starting with a requestand creating the appropriate response”interfa...
 The HttpKernel defines a complex flexible workflow The controller to execute is found via a ControllerResolver «Framew...
$request = Request::createFromGlobals();$dispatcher = new EventDispatcher();// ... add some event listeners, eg: routing, ...
 Any class implementing the ControllerResolverInterface can be usedinterface ControllerResolverInterface{// must return a...
 The Event Dispatcher Component can be useduse SymfonyComponentEventDispatcherEvent;$dispatcher->addListener(foo.action, ...
Taming the beast
 New Core: a standard Simfony app («ezpublish» = «app») «Legacy Stack» isolated in a dedicated directory5/18/2013gg@ez.n...
 New Core: Sf Bundles5/18/2013gg@ez.no Slide 26Refactoring: bundles
use SymfonyComponentHttpFoundationRequest;require_once __DIR__ . /../ezpublish/autoload.php; // set up class autoloadingre...
 Sandbox legacy code in a closure Index.php had to be refactored (from 1100 lines to 20) Logic moved to a php class Se...
Routing
 eZPublish 4 uses a custom MVC implementation Frontend controller: index.php Bootstraps configuration system, logging, ...
5/18/2013gg@ez.no Slide 31Routing: seamless integration
 The ChainRouter from the Sf CMF project is used Routes for new controllers can be declared in different ways In a conf...
Caching
 eZ Publish 4 has a complicated advanced caching system For viewing content, cache is generated on access, invalidated o...
 eZ has a built-in “full-page cache” (stores html on disk) Currently deprecated, in favour of using a caching reverse Pr...
 HTTP Expiration and Validation are used By setting caching headers on response object Integrates with a Gateway Cache ...
REST
 eZ4 had an incomplete REST API Only functionality available: reading content Based on Zeta Components MVC component A...
More info
 Tutorials: http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1 h...
Upcoming SlideShare
Loading in...5
×

Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013

3,420

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
3,420
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
13
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "Symfony HTTP Kernel for refactoring legacy apps: the eZ Publish case study - Pug Milano 2013"

  1. 1. Case study: eZ PublishSymfony2 http kernel for legacy appsGaetano Giunta | PUG Milano | Maggio 2013
  2. 2. The drivers
  3. 3.  Existing codebase is 10 years old  High maintenance cost Started with no unit tests Layers and roles not properly defined / documented OOP before php had Private/protected/static Closures Namespaces Late static binding And much more Not built for an Ajax and REST world5/18/2013gg@ez.no Slide 3Why change?Everyone loves NEW!
  4. 4.  Existing codebase is 10 years old  Widely deployed Well debugged Pitfalls have probably been uncovered by now Proven to scale Well known: Documentation improved over years Tutorials, forums, blogs, aggregators Active community of practitioners Official training courses5/18/2013gg@ez.no Slide 4Why change?Do not forget drawbacks
  5. 5.  Focus on our core business Experience Management Content Management NOT Framework maintenance DurableArchitecture API stability Battle tested / not (only) the latest trend Scalability Lively Community!5/18/2013gg@ez.no Slide 5Picking a framework for a platform rebuild
  6. 6. • Simple Integration with existing API• HMVC (Hierarchical Model View Controller) stack• Decoupled Components• Dependency Injection• Good Template Engine• Extensible, Open, Reliable ;-)5/18/2013gg@ez.no Slide 6Prerequisites
  7. 7. • Home brew• Zeta Components• Zend Framework 2• Symfony 2 (Full Stack!)5/18/2013gg@ez.no Slide 7Candidates
  8. 8. 5/18/2013gg@ez.no Slide 8And the winner is…Title of presentation is a hint, really...
  9. 9. The challenge
  10. 10. Product Management SCRUM Story:«As an existing user, I don’t want to be pissed off by a new #@!$% version!»5/18/2013gg@ez.no Slide 10Backwards compatibility(life sucks)
  11. 11. Product Management SCRUM Story:«As an existing user, I don’t want to be pissed off by a new #@!$% version!»• 100% Data Compatible (same DB scheme)• Possibility to include legacy templates in the new ones• Routing fallback• Load legacy content templates with legacy rules• Settings• Access Symfony services from legacy modules5/18/2013gg@ez.no Slide 11Backwards compatibility: the objectives
  12. 12. Product Management SCRUM Story:«As an existing user, I don’t want to be pissed off by a new #@!$% version!»• 100% Data Compatible (same DB scheme)• Possibility to include legacy templates in the new ones• Routing fallback• Load legacy content templates with legacy rules• Settings• Access Symfony services from legacy modules5/18/2013gg@ez.no Slide 12Backwards compatibility: the objectives
  13. 13. A new architecture
  14. 14. Product Management SCRUM Story:«As an existing user, I don’t want to be pissed off by a new #@!$% version!»• 100% Data Compatible (same DB scheme)• Possibility to include legacy templates in the new ones• Routing fallback• Load legacy content templates with legacy rules• Settings• Access Symfony services from legacy modulesChallenge Accepted5/18/2013gg@ez.no Slide 14BC: the challenge
  15. 15. 5/18/2013gg@ez.no Slide 15Dual-core architecture
  16. 16. Legacy version still works perfectly standalone5/18/2013gg@ez.no Slide 16BC: icing on the cake
  17. 17. Isn’t this what you have been waiting for?The HTTP Kernel
  18. 18. Request => process() => Responseuse SymfonyComponentHttpFoundationRequest;use SymfonyComponentHttpFoundationResponse;$request = Request::createFromGlobals();$input = $request->get(name, World); // allows a default value$response = new Response(Hello . htmlspecialchars($input, ENT_QUOTES, UTF-8));$response->send(); // takes care of http headers The HTTPFoundation Component eases mundane tasks5/18/2013gg@ez.no Slide 18Use the HTTP, LukeA very, very simple frontend controller
  19. 19.  The HTTPKernel component “formalizes the process of starting with a requestand creating the appropriate response”interface HttpKernelInterface{const MASTER_REQUEST = 1;const SUB_REQUEST = 2;public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);} Returns a Response instance The «H» in HMVC: subrequests are baked in from the beginning Looks simple so far, isn’t it?5/18/2013gg@ez.no Slide 19The heart of the application
  20. 20.  The HttpKernel defines a complex flexible workflow The controller to execute is found via a ControllerResolver «Framework» work is done via an event system / event listeners5/18/2013gg@ez.no Slide 20Adding the magic
  21. 21. $request = Request::createFromGlobals();$dispatcher = new EventDispatcher();// ... add some event listeners, eg: routing, security checking// create the controller resolver$resolver = new MyControllerResolver();// instantiate the kernel$kernel = new HttpKernel( $dispatcher, $resolver );$response = $kernel->handle( $request );$response->send();$kernel->terminate( $request, $response );5/18/2013gg@ez.no Slide 21Building a frontend controller
  22. 22.  Any class implementing the ControllerResolverInterface can be usedinterface ControllerResolverInterface{// must return a callablepublic function getController(Request $request);// returns an array of arguments for the controllerpublic function getArguments(Request $request, $controller);} ...5/18/2013gg@ez.no Slide 22Finding the Controller
  23. 23.  The Event Dispatcher Component can be useduse SymfonyComponentEventDispatcherEvent;$dispatcher->addListener(foo.action, $callable); Depending on returned value, workflow might be altered (see docs online) Dispatchedevents:Name Name as constant Argument passed to the listenerkernel.request KernelEvents::REQUEST GetResponseEventkernel.controller KernelEvents::CONTROLLER FilterControllerEventkernel.view KernelEvents::VIEW GetResponseForControllerResultEventkernel.response KernelEvents::RESPONSE FilterResponseEventkernel.terminate KernelEvents::TERMINATE PostResponseEventkernel.exception KernelEvents::EXCEPTION GetResponseForExceptionEvent5/18/2013gg@ez.no Slide 23Adding event listeners
  24. 24. Taming the beast
  25. 25.  New Core: a standard Simfony app («ezpublish» = «app») «Legacy Stack» isolated in a dedicated directory5/18/2013gg@ez.no Slide 25Refactoring: directory layout
  26. 26.  New Core: Sf Bundles5/18/2013gg@ez.no Slide 26Refactoring: bundles
  27. 27. use SymfonyComponentHttpFoundationRequest;require_once __DIR__ . /../ezpublish/autoload.php; // set up class autoloadingrequire_once __DIR__ . /../ezpublish/EzPublishKernel.php;$kernel = new EzPublishKernel( dev, true ); // extends the Sf Kernel class$kernel->loadClassCache(); // a method from parent class$request = Request::createFromGlobals();$response = $kernel->handle( $request );$response->send();$kernel->terminate( $request, $response ); The Kernel class wraps the HTTPKernel It adds a Service Container It allows to register bundles via registerBundles()5/18/2013gg@ez.no Slide 27The final frontend controllerUsing Symfony Full Stack
  28. 28.  Sandbox legacy code in a closure Index.php had to be refactored (from 1100 lines to 20) Logic moved to a php class Separated environment setup from execution and teardown runCallback() sets up the global legacy environment5/18/2013gg@ez.no Slide 28Refactoring: bridging Legacy code
  29. 29. Routing
  30. 30.  eZPublish 4 uses a custom MVC implementation Frontend controller: index.php Bootstraps configuration system, logging, “siteaccess” Controllers are “plain php” files, properly declared Url syntax: http:// site / module / controller / parameters Parameters use a custom format instead of the query string Virtual aliases can be added on top For all content nodes, a nice alias is always generated by the system Good for SEOTechnical debt No DIC anywhere (registry pattern used) No nested controllers No provision for REST / AJAX Implemented ad-hoc in many plugins (code/functionality duplication) Policies are tied to controllers, not to the underlying content model5/18/2013gg@ez.no Slide 30Routing
  31. 31. 5/18/2013gg@ez.no Slide 31Routing: seamless integration
  32. 32.  The ChainRouter from the Sf CMF project is used Routes for new controllers can be declared in different ways In a configuration file app/config/routing.yml Mybundle/Resources/config/routing.yml (loaded from main routing file) Via annotations (phpdoc comments) needs the SensioFrameworkExtraBundle bundle Command line to dump themphp app/console router:debug Maximum flexibility for parameters: required/optionsl, default values,validation, restrict http method, extra support for locale and format, ...5/18/2013gg@ez.no Slide 32Routing: how it works
  33. 33. Caching
  34. 34.  eZ Publish 4 has a complicated advanced caching system For viewing content, cache is generated on access, invalidated on editing TTL = infinite When editing a content, cache is also invalidated for all related contents Extra invalidation rules can be configured Can be set up to be pregenerated at editing time (tradeoff: editing speed) Cache keys include policies of current user, query string, custom session data “Cache-blocks” can also be added anywhere in the templates Expiry rules can be set on each block, TTL-based or content-editing based Breaks mvc principle Most powerful AND misunderstoodfeature in the CMS5/18/2013gg@ez.no Slide 34eZ4 Caching: basics
  35. 35.  eZ has a built-in “full-page cache” (stores html on disk) Currently deprecated, in favour of using a caching reverse Proxy Performances same if not better Delegate maintenance of part of the stack (Varnish, Squid) Holy grail of caching: high TTL and support for PURGE command1. When RP requests page from server, he gets a high TTL => cache page forever2. When page changes, server tells to RP to purge that url from cache Best reduction in number of requests to server while always showing fresh data Downside: extremely hard to cache pages for connected users ESI support as well Hard to make efficient, as eZ can not regenerate an ESI block without full pagecontext5/18/2013gg@ez.no Slide 35eZ4 Caching: integration with Reverse Proxies
  36. 36.  HTTP Expiration and Validation are used By setting caching headers on response object Integrates with a Gateway Cache (a.k.a Reverse Proxy) Native (built-in, php)$kernel = new Kernel(prod, false);$kernel = new HTTPCache($kernel); External (Varnish, Squid, ...) Native support for ESI Using {{ render_esi() }} in twig5/18/2013gg@ez.no Slide 36Symfony Caching: basics
  37. 37. REST
  38. 38.  eZ4 had an incomplete REST API Only functionality available: reading content Based on Zeta Components MVC component A new API has been implemented Full reading and writing of content is possible All “dictionary” data is also available Content-type for response can be JSON or XML (with an XSD!) Fully restful Usage of all HTTP verbs (and then some: PATCH) Respect http headers of request (eg: “Accept”) HATEOAS: use urls as resource ids No separate request handling framework needed: pure Symfony routing Bonus points: a client for the REST API, implements the same interfaces exposedby the local PHP API – network transparency!!!5/18/2013gg@ez.no Slide 40REST API
  39. 39. More info
  40. 40.  Tutorials: http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1 http://symfony.com/doc/current/components/http_kernel/introduction.html Sf2 book – jolly good looking docs:http://symfony.com/doc/current/book/index.html eZ Publish: Community: http://share.ez.no Source code: https://github.com/ezsystems API docs: http://pubsvn.ez.no/preview.html Contact me: @gggeek, gaetano.giunta@ez.no5/18/2013gg@ez.no Slide 42The usual suspects
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×