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. 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
  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
  3. DI Benefits • Dependency Inversion Principle • Clear separation of

    object instantiation and object usage • Object delivery
  4. 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
  5. “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”
  6. /** * @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
  7. /** * @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
  8. /** * @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
  9. $user = $injector->getInstance('UserInterface'); 4. Get an object graph from the

    $injector User Renderer Renderer Interface depends A B
  10. class RendererModule extends AbstractModule { protected function configure() { $this

    ->bind('RenderInterface') ->to('HalRenderer') ->in(Scope::SINGLETON); } } Only use concrete classes in compilation
  11. Only abstraction in runtime /** * @Inject */ public function

    __construct(RenderInterface $renderer) {
  12. 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.”
  13. Application is root object retrieved with injector: $injector = Injector::create([new

    AppModule($context)]]; $app = $injector->getInstance(‘AppInterface’);
  14. You get a application object graph. huge, but can be

    stored one single root value $app
  15. $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
  16. What is AOP? Cache Log Auth A programming paradigm that

    aims to increase modularity by allowing the separation of cross-cutting concerns
  17. /** * @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; } }
  18. M C Cache Cache is called by method invocation, If

    the cache is warm the model is never called. $obj->read(2); Miss !
  19. interface MethodInterceptor { public function invoke(MethodInvocation $invocation); } $invocation->proceed(); //

    invoked method $object = $invocation->getThis(); // get source object $args = $invocation->getArgs(); // get arguments
  20. 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
  21. 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
  22. <?php 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.
  23. 2nd framework: Aspect Oriented Framework • AOP alliance standard •

    Layering by context • Type safe • Runtime injection
  24. 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.
  25. 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)
  26. $user = $resource ->get ->uri('app://self/user') ->withQuery(['id' => 1]) ->eager ->request();

    var_dump($user->body); // Array // ( // [name] => John // [age] => 15 //) echo $user; // <div id=”name”>John</div> // <div id=”age”>15</div> public function onGet($id) { $this[‘name’] = ‘John’; $this[‘age’] = 15; return $this; } User Profile Friend
  27. $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
  28. 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
  29. • API is hub • API is core value API

    driven development DB Mobil e Web API Cloud Moc k URI API API
  30. Performance • annotation ? dependency injection ? method interception ?

    DSL ? named parameter ? • Fast • cache all compiled object • http friendly architecture
  31. Hard spot / Soft spot • DI configure hardspot. QFSTZTUFN

    • Aop configure softspot, change on request
  32. Connecting frameworks • DI - object as dependency • AOP

    - domain logic to application logic • Hypermedia - resource to resource