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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

  5. instead, it offers three object frameworks.

    View full-size slide

  6. What is a framework ?

    View full-size slide

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

    View full-size slide

  8. 3.hypermedia framework

    View full-size slide

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

    View full-size slide

  10. 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 full-size slide

  11. “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 full-size slide

  12. Ray.DI
    Dependency Injection Framework

    View full-size slide

  13. /**
    * @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 full-size slide

  14. /**
    * @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 full-size slide

  15. /**
    * @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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  20. 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 full-size slide

  21. Application = one root object

    View full-size slide

  22. Application class

    View full-size slide

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

    View full-size slide

  24. Application has dependency.

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  28. $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 full-size slide

  29. Aspect Oriented Programing

    View full-size slide

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

    View full-size slide

  31. /**
    * @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 full-size slide

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

    View full-size slide

  33. Aspects
    Core Concern Cross Cutting Concern
    Separation

    View full-size slide

  34. Rock Concert Example

    View full-size slide

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

    View full-size slide

  36. 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 full-size slide

  37. 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 full-size slide

  38. 4JNQMZBOOPUBUF
    UIFODSFBUFZPVSCJOEJOH
    #JOE

    View full-size slide

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

    View full-size slide

  40. API Log
    Client Valid Auth

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  43. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  46. 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 full-size slide

  47. 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 full-size slide

  48. $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 full-size slide

  49. $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 full-size slide

  50. 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 full-size slide

  51. Order Payment
    hyper reference: pay
    HyperMedia Driven API

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  61. “Zen” framework

    View full-size slide

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

    View full-size slide