A resource orientated framework
using the DI /AOP/REST Triangle
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
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
BEAR.Sunday is an application framework.
But it offers no libraries.
(We have good stuff)
instead, it offers three object frameworks.
What is a framework ?
“imposes a set of design constraints on end-user code.”
3.hypermedia framework
DI
AOP
REST
DI Benefits
• Dependency Inversion Principle
• Clear separation of object instantiation and object
usage
• Object delivery
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
“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”
Ray.DI
Dependency Injection Framework
/**
* @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
/**
* @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
/**
* @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
$user = $injector->getInstance('UserInterface');
4. Get an object graph from the $injector
User Renderer
Renderer Interface
depends
A
B
procedural
object orietned
compilation
runtime
Implement the structure, not a procedure
class RendererModule extends AbstractModule
{
protected function configure()
{
$this
->bind('RenderInterface')
->to('HalRenderer')
->in(Scope::SINGLETON);
}
}
Only use concrete classes in compilation
Only abstraction in runtime
/**
* @Inject
*/
public function __construct(RenderInterface $renderer)
{
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.”
Application = one root object
Application class
Application is root object
retrieved with injector:
$injector = Inject::create([new AppModule($context)]];
$app = $injector->getInstance(‘AppInterface’);
Application has dependency.
Dependencies have other dependencies
Each object either contains or belongs to.
You get a application object graph.
huge, but can be stored one single root value $app
Application can be serialized.
$app can be serialized and stored
Injection is reused beyond requests.
$app
Object
i/f
i/f
Object
i/f i/f
ObjectRouter
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
Aspect Oriented Programing
What is AOP?
Cache
Log
Auth
A programming paradigm that aims to increase modularity
by allowing the separation of cross-cutting concerns
/**
* @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;
}
}
MC
I I
AOP
MC
Cache
Cache is called by method invocation,
If the cache is warm the model is never called.
$obj->read(2);
Miss !
Aspects
Core Concern Cross Cutting Concern
Separation
Rock Concert Example
interface MethodInterceptor
{
public function invoke(MethodInvocation $invocation);
}
$invocation->proceed(); // invoked method
$object = $invocation->getThis(); // get source object
$args = $invocation->getArgs(); // get arguments
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
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
Simply annotate,
then create your binding
Bind
Layering by context
• MVC, Is 3 enough ?
APIClient
API LogClient Valid Auth
API Log
@Valid
/admin DELETE
Client Valid Auth
Aspect layering by context
Model
Cache
Form
Transaction
Auth
Validation
<?php
class SandboxResourcePageIndexRay0000000071f9ab280000000033fb446fAop extends
SandboxResourcePageIndex implements RayAopWeavedInterface
{
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 RayAopReflectiveMethodInvocation(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.
Runtime injection by aspect
• method / parameter lookup
• test friendly
2nd framework: Aspect Oriented Framework
• AOP alliance standard
• Layering by context
• Type safe
• Runtime injection
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.
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)
$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
$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
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
Order Payment
hyper reference: pay
HyperMedia Driven API
The key of success of web
• URI
• Unified Interface
• Hyperlink
• API is hub
• API is core value
API driven development
DB Mobil
e
Web
API
Cloud
Moc
k
URI
API
API
http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
Layered Resource
UI
Mobile
Web
Page Resource
App script
App Resource
Entity
Performance
• annotation ? dependency injection ?
method interception ? DSL ? named parameter ?
• Fast
• cache all compiled object
• http friendly architecture
Scale
• “model proxy” pattern
• ‘app://self/blog/entry’ can be anything.
• contextual injection makes db scale easy
Hard spot / Soft spot
• DI configure hardspot. per system
• Aop configure softspot, change on request
Connecting frameworks
• DI - object as dependency
• AOP - domain logic to application logic
• Hypermedia - resource to resource
Abstraction frameworks
• DSL
• Annotation
• URI
• Interface
• Aspects
• Hypermedia
“Zen” framework
Thanks.
Arigato
http://www.flickr.com/photos/stevehoad/4678289858/
@mackstar@koriym

A resource oriented framework using the DI/AOP/REST triangle

  • 1.
    A resource orientatedframework using the DI /AOP/REST Triangle
  • 2.
    About Us • AkihitoKoriyama • 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.
    About Us • AkihitoKoriyama • 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
  • 5.
    BEAR.Sunday is anapplication framework. But it offers no libraries. (We have good stuff)
  • 6.
    instead, it offersthree object frameworks.
  • 7.
    What is aframework ?
  • 8.
    “imposes a setof design constraints on end-user code.”
  • 11.
  • 12.
  • 13.
    DI Benefits • DependencyInversion Principle • Clear separation of object instantiation and object usage • Object delivery
  • 14.
    DIP • Code shoulddepend on things that are at the same or higher level of abstraction • High level policy should not depend on low level details
  • 15.
    “The principle ofdependency 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”
  • 16.
  • 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
  • 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
  • 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
  • 20.
    $user = $injector->getInstance('UserInterface'); 4.Get an object graph from the $injector User Renderer Renderer Interface depends A B
  • 21.
  • 22.
    class RendererModule extendsAbstractModule { protected function configure() { $this ->bind('RenderInterface') ->to('HalRenderer') ->in(Scope::SINGLETON); } } Only use concrete classes in compilation
  • 23.
    Only abstraction inruntime /** * @Inject */ public function __construct(RenderInterface $renderer) {
  • 24.
    DI Best practice “Yourcode should deal directly with the Injector as little as possible. Instead, you want to bootstrap your application by injecting one root object.”
  • 25.
    Application = oneroot object
  • 26.
  • 28.
    Application is rootobject retrieved with injector: $injector = Inject::create([new AppModule($context)]]; $app = $injector->getInstance(‘AppInterface’);
  • 29.
  • 31.
    Dependencies have otherdependencies Each object either contains or belongs to.
  • 32.
    You get aapplication object graph. huge, but can be stored one single root value $app
  • 33.
    Application can beserialized. $app can be serialized and stored Injection is reused beyond requests.
  • 34.
    $app Object i/f i/f Object i/f i/f ObjectRouter 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
  • 35.
  • 36.
    What is AOP? Cache Log Auth Aprogramming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns
  • 37.
    /** * @Cache */ public functiononGet($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; } }
  • 38.
  • 39.
    MC Cache Cache is calledby method invocation, If the cache is warm the model is never called. $obj->read(2); Miss !
  • 40.
    Aspects Core Concern CrossCutting Concern Separation
  • 41.
  • 42.
    interface MethodInterceptor { public functioninvoke(MethodInvocation $invocation); } $invocation->proceed(); // invoked method $object = $invocation->getThis(); // get source object $args = $invocation->getArgs(); // get arguments
  • 43.
    class Transactional implementsMethodInterceptor { 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
  • 44.
    class CacheInterceptor implementsMethodInterceptor { 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
  • 45.
  • 46.
    Layering by context •MVC, Is 3 enough ?
  • 47.
  • 48.
  • 49.
  • 50.
    Aspect layering bycontext Model Cache Form Transaction Auth Validation
  • 51.
    <?php class SandboxResourcePageIndexRay0000000071f9ab280000000033fb446fAop extends SandboxResourcePageIndeximplements RayAopWeavedInterface { 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 RayAopReflectiveMethodInvocation(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.
  • 52.
    Runtime injection byaspect • method / parameter lookup • test friendly
  • 53.
    2nd framework: AspectOriented Framework • AOP alliance standard • Layering by context • Type safe • Runtime injection
  • 55.
    Hypermedia framework forobject 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.
  • 56.
    class Author extendsResourceObject { 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)
  • 57.
    $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
  • 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
  • 59.
    class Order extendsResourceObject { /** * * @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
  • 60.
    Order Payment hyper reference:pay HyperMedia Driven API
  • 61.
    The key ofsuccess of web • URI • Unified Interface • Hyperlink
  • 62.
    • API ishub • API is core value API driven development DB Mobil e Web API Cloud Moc k URI API API
  • 63.
  • 64.
  • 69.
    Performance • annotation ?dependency injection ? method interception ? DSL ? named parameter ? • Fast • cache all compiled object • http friendly architecture
  • 70.
    Scale • “model proxy”pattern • ‘app://self/blog/entry’ can be anything. • contextual injection makes db scale easy
  • 71.
    Hard spot /Soft spot • DI configure hardspot. per system • Aop configure softspot, change on request
  • 72.
    Connecting frameworks • DI- object as dependency • AOP - domain logic to application logic • Hypermedia - resource to resource
  • 73.
    Abstraction frameworks • DSL •Annotation • URI • Interface • Aspects • Hypermedia
  • 75.
  • 76.