Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Patterns for Software Modernization (IPC14)

Patterns for Software Modernization (IPC14)

Patterns like MVC and dependency injection help the development teams of modern applications frameworks to deliver an extendable and maintainable product. Why shouldn't those patterns help you modernizing your legacy application? In this session, we will have a look at ways to apply modern patterns to legacy code by using small refactoring steps.

Alexander M. Turek

October 28, 2014
Tweet

More Decks by Alexander M. Turek

Other Decks in Programming

Transcript

  1. [AMT] Alexander M. Turek about:me freelance software developer. builds web

    applications with php. likes the Symfony framework. lives in Munich Berlin, Germany. 2
  2. [AMT] Alexander M. Turek A Web Application 3 Data Layer

    Business Logic HTTP Layer HTTP-Request HTTP-Response SQL
 Database
  3. [AMT] Alexander M. Turek The Application A Legacy Application 4

    HTTP-Request HTTP-Response Magic SQL
 Database
  4. [AMT] Alexander M. Turek A Legacy Web Project • The

    team maintains an old software system. • The system still fulfills current requirements. • The company earns money with the system. • The original developers have left the team. 5
  5. [AMT] Alexander M. Turek Legacy Maintenance • poorly tested code

    base • poor code quality • requirements got lost over time • implementing new features takes much time • high bug rate 6
  6. [AMT] Alexander M. Turek Unclear Dependencies 7 If I change

    one component, I might accidentally break another.
  7. [AMT] Alexander M. Turek Legacy Dilemma 8 • Developers work

    differently on legacy code. • Tendency of coding as it has always been done. • No tests are written, as the codebase is considered to be untestable.
  8. [AMT] Alexander M. Turek Continuous Under-Engineering 9 As you attempt

    to deliver future releases, you go slower and slower as the junky code multiplies, until people lose faith in the system, the programmers, and even the process that got everyone into this position. Joshua Kerievsky: Refactoring to Patterns
  9. [AMT] Alexander M. Turek Refactoring 11 Refactoring is the process

    of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure. Martin Fowler: Refactoring – Improving the Design of Existing Code
  10. [AMT] Alexander M. Turek Reasons for Refactoring 12 • Make

    it easier to add new code. • Improve the design of existing code. • Gain a better understanding of code. • Make coding less annoying.
  11. [AMT] Alexander M. Turek Refactor Everything? 13 ✓ improved code

    quality - time-consuming - error-prone - no direct business value
  12. [AMT] Alexander M. Turek Fun Fact 14 Your application probably

    conTains much code that „just works“.
  13. [AMT] Alexander M. Turek Refactoring for Changes 15 • If

    you need to change a legacy component, refactor it first. Afterwards, implement the new feature. • Gain understanding for the code. • Make the code testable. • Make the code changeable.
  14. [AMT] Alexander M. Turek Application Split 21 Data Layer Business

    Logic HTTP Layer Data Layer Business Logic HTTP Layer Master Database Application 1 Application 2 Authentication Service …
  15. [AMT] Alexander M. Turek Application Split A monolithic application
 is

    split into smaller parts. • standalone applications • common services 22 Master Database Application 1 Application 2 Authentication Service …
  16. [AMT] Alexander M. Turek Split Applications When… • You can

    isolate an application that only reads from the database. • You can isolate an application that is small enough to be rewritten completely. • You can replace a part of your application by an
 off-the-shelf solution (open source or commercial). 23
  17. [AMT] Alexander M. Turek Example: Web Shop • shop frontend


    (search, product pages) • checkout process • administration area • data import • data warehouse • CRM 24 read-only! use third-party software?
  18. [AMT] Alexander M. Turek Strangler Application • A new application

    is built to server a small subset of routes of the legacy application. • A router directs matching requests to the new application. • Repeat. 26 Legacy Data Layer Business Logic HTTP Layer Request Routing Event Interception
  19. [AMT] Alexander M. Turek Use Strangulation When… • You need

    to keep old URLs working (SEO, inbound traffic). • You expect future changes to a subset of your routes. • You expect all new routes being added. • You want to enable an application split. • You want to migrate to a new full-stack framework. • You migrate between platforms. 27
  20. [AMT] Alexander M. Turek Routing:
 Reverse Proxy • Legacy and

    strangler are listening on different
 servers/ports/vhosts/… • The proxy decides which application should serve an incoming request based on configured rules. ✓easy rollback ✓isolated environments - ruleset complexity - infrastructure dependency 28
  21. [AMT] Alexander M. Turek Routing:
 Web Server • Legacy and

    Strangler are deployed to different directories on the web server. • The HTTP server decides which application should server an incoming request (rewrite rules). ✓easy rollback - ruleset complexity - dependency on web server software 29
  22. [AMT] Alexander M. Turek Routing:
 Front Controller Script • All

    requests are routed to a front controller script. • front controller boots either legacy or strangler ✓easier to debug than mod_rewrite - no isolated environments - rollback requires deployment 30
  23. [AMT] Alexander M. Turek Routing:
 404 Event Handler • Try

    to serve the request with the strangler app. • Catch the 404 framework event and boot the legacy application. ✓no temporary request router - Booting both apps might cause side-effects. - Performance on legacy routes might suffer. 31
  24. [AMT] Alexander M. Turek Façade Pattern Provides a simplified interface

    to a larger body of code. 32 Source: Wikipedia (CC-BY-SA)
 http://en.wikipedia.org/wiki/File:Example_of_Facade_design_pattern_in_UML.png
  25. [AMT] Alexander M. Turek Façade Pattern Provide legacy functionality. Hide

    legacy complexity. 33 Legacy Business Logic Legacy API
  26. [AMT] Alexander M. Turek Migration By Component • Rewrite a

    legacy component. • Connect the legacy component where ever the old component is used. • Drop the legacy component. • Repeat. 35 Component Legacy
  27. [AMT] Alexander M. Turek Migrate By Component When… • You

    want to introduce 3rd party libraries.
 (Monolog, Symfony HTTP Foundation, Doctrine) • You want to migrate to a component framework.
 (Symfony Components) • You expect future changes to legacy components.
 (new requirements, high bug rate) 36
  28. [AMT] Alexander M. Turek Adapter Pattern AKA Wrapper AKA Translator

    Provide the functionality of a class under a different interface. 37
  29. [AMT] Alexander M. Turek Legacy Wrapper A wrapper simulates the

    interface of the old component. Only minimal changes to the legacy system required. 38 New Component Legacy Application Wrapper
  30. [AMT] Alexander M. Turek class Customer {
 public $id;
 public

    function getName() {
 $db = new mysqli(
 'db.example.com', 'root', 'p741n-t3xt'
 );
 $res = $db->query(
 "SELECT name FROM customer WHERE id = $id;"
 );
 list($name) = $res->fetch_row();
 return $name;
 }
 } Database Dependency 41
  31. [AMT] Alexander M. Turek Hard-Wired Dependencies 42 • Dependencies... •

    ... are not changeable. • ... are not configurable. • ... will always be tested with your code. • ... are not explicit. new MyClass
  32. [AMT] Alexander M. Turek Global State Dependency 43 • Code

    depends on... • ... global variables/functions. • ... static fields/methods. • ... constants.
  33. [AMT] Alexander M. Turek Dependency Injection 44 class Newsletter {


    public function send() {
 $mailer = new Mailer();
 $mailer->send(…);
 }
 }
 class Newsletter {
 private $mailer;
 
 public function __construct(Mailer $mailer) {
 $this->mailer = $mailer;
 }
 
 public function send() {
 $this->mailer->send(…);
 }
 }

  34. [AMT] Alexander M. Turek Dependency Injection • core pattern of

    modern frameworks • make dependencies explicit • allows changing dependencies • improves testability 45
  35. [AMT] Alexander M. Turek Dependency Injection Containers • make the

    assembly of components configurable • service locator pattern • request a service • receive a readily configured object • Not a registry: objects are constructed on demand! 46
  36. [AMT] Alexander M. Turek Dependency Injection Containers • Pimple •

    Symfony DependencyInjection Component • Zend\Di • TYPO3 Flow Object Framework • Aura DI 47
  37. [AMT] Alexander M. Turek Avoiding DI Anti-Patterns • Avoid depending

    on your DI container! • Never make your container global/static. 48
  38. [AMT] Alexander M. Turek Prerequisites • Raise your project to

    PHP 5.3. • … or implement your own DIC. • Introduce class autoloading. 50 not recommended
  39. [AMT] Alexander M. Turek Breaking the Rules • Configure the

    DIC in your bootstrap script. • Make your DIC global accessible – but use this access point in your legacy code only! • Your legacy code may pull your shiny new components from the DIC directly. 51
  40. [AMT] Alexander M. Turek Extracting Dependencies 52 class Customer {


    public $id;
 public function getName() {
 $db = new mysqli('db.example.com', 'root', 'p741n-t3xt');
 $res = $db->query("SELECT name FROM customer WHERE id = $id;");
 if (!$res) {
 $file = fopen('/var/log/myapp/error.log', 'a');
 fwrite($file, sprintf('DB error: %s', $db->error));
 return null;
 }
 list($name) = $res->fetch_row();
 return $name;
 }
 }
  41. [AMT] Alexander M. Turek Service Location via DIC 53 class

    Customer {
 public $id;
 public function getName() {
 global $container;
 
 $db = $container->get('db');
 $res = $db->query("SELECT name FROM customer WHERE id = $id;");
 if (!$res) {
 $container->get('logger')->log(sprintf('DB error: %s', $db->error));
 return null;
 }
 list($name) = $res->fetch_row();
 return $name;
 }
 }
  42. [AMT] Alexander M. Turek Dependency Injection 54 class Customer {


    private $id;
 private $db;
 private $logger;
 
 public function __construct(mysqli $db, Logger $logger, $id) { … }
 
 public function getName() {
 $res = $this->db->query("SELECT name FROM customer WHERE id = $id;");
 if (!$res) {
 $this->logger->log(sprintf('DB error: %s', $db->error));
 return null;
 }
 list($name) = $res->fetch_row();
 return $name;
 }
 }
  43. [AMT] Alexander M. Turek Thank You spam me: [email protected] follow

    me: @derrabus hire me: https://derrabus.de 57