Upload
akihito-koriyama
View
4.192
Download
1
Tags:
Embed Size (px)
DESCRIPTION
https://joind.in/talk/view/9302
Citation preview
A resource orientated frameworkusing 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 RendererRenderer Interface
depends
A
B
procedural
object orietnedcompilation
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
Objecti/f i/f
ObjectRouter
Response
JSON
XML
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
FormTransactionAuth
Validation
<?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.
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) { //... }
public function onDelete($id) { //... }
$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 Mobile
Web
API
Cloud
Mock
URI
API
API
http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
Layered Resource
UIMobile
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