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

A resource orientated framework using the DI/AOP/REST Triangle

A resource orientated framework using the DI/AOP/REST Triangle

DI, AOP and REST are powerful patterns that developers should have in their tool belts. Akihito’s open source BEAR.Sunday framework goes one step further and uses them as the basis for strong architectural design for large scale PHP applications.
With very few components of its own, BEAR.Sunday is a fantastic example of how a framework can be built using existing components and libraries from other frameworks, yet offer even further simplicity beauty and power by holding on to these 3 core design patterns.

phpnw13
http://conference.phpnw.org.uk/phpnw13/schedule/akihito-koriyama-richard-mcintyre/

joindin
http://joind.in/talk/view/9302

Akihito Koriyama

October 06, 2013
Tweet

More Decks by Akihito Koriyama

Other Decks in Technology

Transcript

  1. A resource orientated framework
    using the DI /AOP/REST Triangle

    View Slide

  2. About Us
    • Akihito Koriyama
    • A Tokyo based freelance PHP architect
    • BEAR.Sunday lead, Aura PHP member
    • Richard McIntyre
    • Manchester / leeds based freelance PHP
    developer
    • BEAR.Sunday contributor, Lithium member

    View Slide

  3. About Us
    • Akihito Koriyama
    • A Tokyo based freelance PHP architect
    • BEAR.Sunday lead, Aura PHP member
    • Richard McIntyre
    • Manchester / Leeds based freelance PHP
    developer
    • BEAR.Sunday contributor, Lithium member

    View Slide

  4. View Slide

  5. BEAR.Sunday is an application framework.
    But it offers no libraries.
    (We have good stuff)

    View Slide

  6. instead, it offers three object frameworks.

    View Slide

  7. What is a framework ?

    View Slide

  8. “imposes a set of design constraints on end-user code.”

    View Slide

  9. View Slide

  10. View Slide

  11. 3.hypermedia framework

    View Slide

  12. DI
    AOP
    REST

    View Slide

  13. DI Benefits
    • Dependency Inversion Principle
    • Clear separation of object instantiation and object
    usage
    • Object delivery

    View Slide

  14. DIP
    • Code should depend on
    things that are at the same
    or higher level of
    abstraction
    • High level policy should not
    depend on low level details

    View Slide

  15. “The principle of dependency inversion is at the
    root of many of the benefits claimed for object-
    oriented technology.
    Its proper application is necessary for the creation
    of reusable frameworks”

    View Slide

  16. Ray.DI
    Dependency Injection Framework

    View Slide

  17. /**
    * @Inject
    */
    public function __construct(RenderInterface $renderer)
    {
    ...
    1.annotate at injection point
    class RendererModule extends AbstractModule
    {
    protected function configure()
    {
    $this->bind('RenderInterface')
    ->to('HalRenderer')
    ->in(Scope::SINGLETON);
    }
    $injector = Inject::create([new RendererModule]];
    2 bind abstraction to concretion
    3.create an injector

    View Slide

  18. /**
    * @Inject
    */
    public function __construct(RenderInterface $renderer)
    {
    ...
    1.annotate at injection point
    class RendererModule extends AbstractModule
    {
    protected function configure()
    {
    $this->bind('RenderInterface')
    ->to('HalRenderer')
    ->in(Scope::SINGLETON);
    }
    $injector = Inject::create([new RendererModule]];
    2. bind abstraction to concretion
    3.create an injector

    View Slide

  19. /**
    * @Inject
    */
    public function __construct(RenderInterface $renderer)
    {
    ...
    1.annotate at injection point
    class RendererModule extends AbstractModule
    {
    protected function configure()
    {
    $this->bind('RenderInterface')
    ->to('HalRenderer')
    ->in(Scope::SINGLETON);
    }
    $injector = Inject::create([new RendererModule]];
    2 bind abstraction to concretion
    3.create an injector

    View Slide

  20. $user = $injector->getInstance('UserInterface');
    4. Get an object graph from the $injector
    User Renderer
    Renderer Interface
    depends
    A
    B

    View Slide

  21. QSPDFEVSBM
    PCKFDUPSJFUOFE
    DPNQJMBUJPO
    SVOUJNF
    Implement the structure, not a procedure

    View Slide

  22. class RendererModule extends AbstractModule
    {
    protected function configure()
    {
    $this
    ->bind('RenderInterface')
    ->to('HalRenderer')
    ->in(Scope::SINGLETON);
    }
    }
    Only use concrete classes in compilation

    View Slide

  23. Only abstraction in runtime
    /**
    * @Inject
    */
    public function __construct(RenderInterface $renderer)
    {

    View Slide

  24. DI Best practice
    “Your code should deal directly with the Injector
    as little as possible. Instead, you want to bootstrap
    your application by injecting one root object.”

    View Slide

  25. Application = one root object

    View Slide

  26. Application class

    View Slide

  27. View Slide

  28. Application is root object
    retrieved with injector:
    $injector = Injector::create([new AppModule($context)]];
    $app = $injector->getInstance(‘AppInterface’);

    View Slide

  29. Application has dependency.

    View Slide

  30. View Slide

  31. Dependencies have other dependencies
    Each object either contains or belongs to.

    View Slide

  32. You get a application object graph.
    huge, but can be stored one single root value $app

    View Slide

  33. "QQMJDBUJPODBOCFTFSJBMJ[FE
    $app can be serialized and stored
    Injection is reused beyond requests.

    View Slide

  34. $app
    Object
    i/f
    i/f
    Object
    i/f i/f
    Object
    Router
    Response
    JSON
    XM
    L
    1st framework: DI Framework
    • annotations based DI framework w/ binding DSL
    • compilation / runtime separation
    • use only “plug/abstraction” at runtime
    • application a single large cached object
    • focus on structure not behavior

    View Slide

  35. Aspect Oriented Programing

    View Slide

  36. What is AOP?
    Cache
    Log
    Auth
    A programming paradigm that aims to increase modularity
    by allowing the separation of cross-cutting concerns

    View Slide

  37. /**
    * @Cache
    */
    public function onGet($id)
    {
    // ...
    $this->body = $stmt->fetchAll(PDO::FETCH_ASSOC);
    return $this;
    }
    class Post extends AppModel {
    public function newest() {
    $result = Cache::read('newest_posts', 'longterm');
    if (!$result) {
    $result = $this->find('all');
    Cache::write('newest_posts', $result, 'longterm');
    }
    return $result;
    }
    }

    View Slide

  38. M
    C
    I I
    AOP

    View Slide

  39. M
    C
    Cache
    Cache is called by method invocation,
    If the cache is warm the model is never called.
    $obj->read(2);
    Miss !

    View Slide

  40. Aspects
    Core Concern Cross Cutting Concern
    Separation

    View Slide

  41. Rock Concert Example

    View Slide

  42. interface MethodInterceptor
    {
    public function invoke(MethodInvocation $invocation);
    }
    $invocation->proceed(); // invoked method
    $object = $invocation->getThis(); // get source object
    $args = $invocation->getArgs(); // get arguments

    View Slide

  43. class Transactional implements MethodInterceptor
    {
    public function invoke(MethodInvocation $invocation)
    {
    $object = $invocation->getThis();
    $ref = new ReflectionProperty($object, 'db');
    $ref->setAccessible(true);
    $db = $ref->getValue($object);
    $db->beginTransaction();
    try {
    $invocation->proceed();
    $db->commit();
    } catch (Exception $e) {
    $db->rollback();
    }
    }
    }
    `Transactional`interceptor
    Core Concern
    Cross Cutting Concern

    View Slide

  44. class CacheInterceptor implements MethodInterceptor
    {
    public function invoke(MethodInvocation $invocation)
    {
    $obj = $invocation->getThis();
    $args = $invocation->getArguments();
    $id = get_class($obj) . serialize($args);
    $saved = $this->cache->fetch($id);
    if ($saved) {
    return $saved;
    }
    $result = $invocation->proceed();
    $this->cache->save($id, $result);
    return $result;
    }
    }
    `Cache`interceptor
    Core Concern
    Cross Cutting Concern

    View Slide

  45. 4JNQMZBOOPUBUF
    UIFODSFBUFZPVSCJOEJOH
    #JOE

    View Slide

  46. Layering by context
    • MVC, Is 3 enough ?

    View Slide

  47. API
    Client

    View Slide

  48. API Log
    Client Valid Auth

    View Slide

  49. API Log
    !7BMJE
    BENJO %&-&5&
    Client Valid Auth

    View Slide

  50. Aspect layering by context
    Model
    Cache
    Form
    Transaction
    Auth
    Validation

    View Slide

  51. class SandboxResourcePageIndexRay0000000071f9ab280000000033fb446fAop extends
    Sandbox\Resource\Page\Index implements Ray\Aop\WeavedInterface
    {
    private $rayAopIntercept = true;
    public $rayAopBind;
    public function onGet()
    {
    // native call
    if (!isset($this->rayAopBind[__FUNCTION__])) {
    return call_user_func_array('parent::' . __FUNCTION__, func_get_args());
    }
    // proceed source method from interceptor
    if (!$this->rayAopIntercept) {
    $this->rayAopIntercept = true;
    return call_user_func_array('parent::' . __FUNCTION__, func_get_args());
    }
    // proceed next interceptor
    $this->rayAopIntercept = false;
    $interceptors = $this->rayAopBind[__FUNCTION__];
    $annotation = isset($this->rayAopBind->annotation[__FUNCTION__]) ? $this->rayAopBind
    >annotation[__FUNCTION__] : null;
    $invocation = new \Ray\Aop\ReflectiveMethodInvocation(array($this, __FUNCTION__),
    func_get_args(), $interceptors, $annotation);
    return $invocation->proceed();
    }
    Under the hood: Method interception sub class is created
    in order enable this interception and keep type safety.

    View Slide

  52. Runtime injection by aspect
    • method / parameter lookup
    • test friendly

    View Slide

  53. 2nd framework: Aspect Oriented Framework
    • AOP alliance standard
    • Layering by context
    • Type safe
    • Runtime injection

    View Slide

  54. View Slide

  55. Hypermedia framework for object as a service
    It allows objects to have RESTful web service benefits
    such as client-server, uniform interface, statelessness,
    resource expression with mutual connectivity and
    layered components.

    View Slide

  56. class Author extends ResourceObject
    {
    public $code = 200;
    public $headers = [];
    public $body = [];
    /**
    * @Link(rel="blog", href="app://self/blog/post?author_id={id}")
    */
    public function onGet($id)
    {
    ... // change own state
    return $this;
    }
    public function onPost($name)
    {
    $this->code = 201; // created
    return $this;
    }
    public function onPut($id, $name)

    View Slide

  57. $user = $resource
    ->get
    ->uri('app://self/user')
    ->withQuery(['id' => 1])
    ->eager
    ->request();
    var_dump($user->body);
    // Array
    // (
    // [name] => John
    // [age] => 15
    //)
    echo $user;
    // John
    // 15
    public function onGet($id)
    {
    $this[‘name’] = ‘John’;
    $this[‘age’] = 15;
    return $this;
    }
    User
    Profile
    Friend

    View Slide

  58. $order = $resource
    ->post
    ->uri('app://self/order')
    ->withQuery(['drink' => 'latte'])
    ->eager
    ->request();
    $payment = [
    'credit_card_number' => '123456789',
    'expires' => '07/07',
    'name' => 'Koriym',
    'amount' => '4.00'
    ];
    // Now use a hyperlink to pay
    $response = $resource->href('pay', $payment);
    echo $response->code; // 201
    Hypermedia as the Engine of Application State

    View Slide

  59. class Order extends ResourceObject
    {
    /**
    *
    * @Link(rel="pay", method="put",
    href="app://self/payment{?order_id,card_number,amount}")
    */
    public function onPost($drink)
    {
    // data store here
    $this['drink'] = $drink;
    $this['order_id'] = $orderId;
    // created
    $this->code = 201;
    return $this;
    }
    Hypermedia as the Engine of Application State

    View Slide

  60. Order Payment
    hyper reference: pay
    HyperMedia Driven API

    View Slide

  61. The key of success of web
    • URI
    • Unified Interface
    • Hyperlink

    View Slide

  62. • API is hub
    • API is core value
    API driven development
    DB Mobil
    e
    Web
    API
    Cloud
    Moc
    k
    URI
    API
    API

    View Slide

  63. http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

    View Slide

  64. Layered Resource
    UI
    Mobile
    Web
    Page Resource
    App script
    App Resource
    Entity

    View Slide

  65. View Slide

  66. View Slide

  67. View Slide

  68. View Slide

  69. Performance
    • annotation ? dependency injection ?
    method interception ? DSL ? named parameter ?
    • Fast
    • cache all compiled object
    • http friendly architecture

    View Slide

  70. Scale
    • “model proxy” pattern
    • ‘app://self/blog/entry’ can be anything.
    • contextual injection makes db scale easy

    View Slide

  71. Hard spot / Soft spot
    • DI configure hardspot. QFSTZTUFN
    • Aop configure softspot, change on request

    View Slide

  72. Connecting frameworks
    • DI - object as dependency
    • AOP - domain logic to application logic
    • Hypermedia - resource to resource

    View Slide

  73. Abstraction frameworks
    • DSL
    • Annotation
    • URI
    • Interface
    • Aspects
    • Hypermedia

    View Slide

  74. View Slide

  75. “Zen” framework

    View Slide

  76. Thanks.
    Arigato
    http://www.flickr.com/photos/stevehoad/4678289858/
    @mackstar
    @koriym

    View Slide