+ All Categories
Home > Software > Dutch PHP Conference 2015 - The quest for global design principles

Dutch PHP Conference 2015 - The quest for global design principles

Date post: 03-Aug-2015
Category:
Upload: matthiasnoback
View: 475 times
Download: 2 times
Share this document with a friend
98
Global Design Principles Matthias Noback Dutch PHP Conference, Amsterdam, June 26 th , 2015 The Quest for
Transcript
Page 1: Dutch PHP Conference 2015 - The quest for global design principles

Global Design

Principles

Matthias Noback Dutch PHP Conference, Amsterdam, June 26th, 2015

The Quest for

Page 2: Dutch PHP Conference 2015 - The quest for global design principles

Stability

Change

Page 3: Dutch PHP Conference 2015 - The quest for global design principles

Stability versus change

• Backwards compatibility of APIs

• Semantic versioning

• HTTP status codes

• XML schemas

Page 4: Dutch PHP Conference 2015 - The quest for global design principles

For internal APIs

only!

Page 5: Dutch PHP Conference 2015 - The quest for global design principles
Page 6: Dutch PHP Conference 2015 - The quest for global design principles

The key is in the concept of communication

ReceiverSender

Page 7: Dutch PHP Conference 2015 - The quest for global design principles

What's being communicated?

Message

Type ("BookHotel")

Value ("Inntel Hotels", "25-06-2015", "27-06-2015")

Page 8: Dutch PHP Conference 2015 - The quest for global design principles

Message flavours

Command Event

Imperative "BookHotel"

Informational "HotelWasBooked"

Page 9: Dutch PHP Conference 2015 - The quest for global design principles

Message flavours

Query Document

Interrogatory "GetRecentBookings"

Neutral "BookingsCollection"

Page 10: Dutch PHP Conference 2015 - The quest for global design principles

Sender

Construct the message

Message

Translate

Construct

(Prepare fortransport)

Page 11: Dutch PHP Conference 2015 - The quest for global design principles

Receiver

Deconstruct the message

(Unwrap)

Translate

Construct

Page 12: Dutch PHP Conference 2015 - The quest for global design principles

Global design principles can be

discovered

if we recognise the fact that communication between objects and applications are (more or less) equal

Page 13: Dutch PHP Conference 2015 - The quest for global design principles

Communication between objects

• Calling a function is like sending a message

• The function and its parameters are the message type

• The arguments constitute the value of the message

Page 14: Dutch PHP Conference 2015 - The quest for global design principles

class AccountService!{!! public function deposit($accountId, Money $amount) ! {!! ! ...!! }!}

Opportunity for sending a message

Inter-object communication

Page 15: Dutch PHP Conference 2015 - The quest for global design principles

$money = new Money(20000, 'EUR');!$userId = 123;!!$accountService->deposit(!! $userId, !! $money!);

Translation

The sender prepares the message for the receiver

Prepare the message

Send the message

Page 16: Dutch PHP Conference 2015 - The quest for global design principles

Communication between applications

• An HTTP request is a message

• The HTTP method and URI are the type of the message

• The HTTP request body constitutes the value of the message

Or AMQP, Stomp, Gearman, ...

Page 17: Dutch PHP Conference 2015 - The quest for global design principles

PUT /accounts/deposit/ HTTP/1.1!Host: localhost!!{!! "accountId": "123",!! "currency": "EUR",!! "amount": 20000!}

Inter-application communication

Opportunity for sending a message

Page 18: Dutch PHP Conference 2015 - The quest for global design principles

$uri ='/accounts/deposit/';!!$data = json_encode([!! 'accountId' => 123!! 'currency' => 'EUR',!! 'amount' => 20000!]);!!$httpClient!! ->createRequest('PUT', $uri, $data)!! ->send();

Translation

Prepare the message

Send the message

Page 19: Dutch PHP Conference 2015 - The quest for global design principles

/** @Route("/accounts/deposit") */!function depositAction(Request $request)!{!! $request = json_decode($request->getContent());!! $money = new Money(!! ! $request['amount'],!! ! $request['currency']!! );!!! $this->get('account_service')->deposit(!! ! ! $request['accountId'],!! ! ! $money!! ! );!!! return new Response(null, 200);!}!

Translation

Prepare the message

Send the message

Page 20: Dutch PHP Conference 2015 - The quest for global design principles

Global design principles

Should be applicable to both inter-object and inter-application communication

Page 21: Dutch PHP Conference 2015 - The quest for global design principles

Part IStability

III Implementation

II Information

Page 22: Dutch PHP Conference 2015 - The quest for global design principles

Sender Receiver

Communication

Dependency relation!

Page 23: Dutch PHP Conference 2015 - The quest for global design principles

Stable Unstable

What is safer?

Unstable Stable

OR

Page 24: Dutch PHP Conference 2015 - The quest for global design principles

To be stable"Steady in position or balance;

firm"

Page 25: Dutch PHP Conference 2015 - The quest for global design principles

Irresponsible

What makes something unstable?

1. Nothing depends on it

It doesn't need to stay the same for anyone

Page 26: Dutch PHP Conference 2015 - The quest for global design principles

Dependent

What makes something unstable?

2. It depends on many things

Any of the dependencies could change at any time

Page 27: Dutch PHP Conference 2015 - The quest for global design principles

Stability is not a quality of one thing

It emerges from the environment of that thing

Page 28: Dutch PHP Conference 2015 - The quest for global design principles

Responsible

What makes something stable?

1. Many things depend on it

It has to stay the same, because change would break dependents

Page 29: Dutch PHP Conference 2015 - The quest for global design principles

Independent

What makes something stable?

2. It depends on nothing

It is not affected by changes in anything

Page 30: Dutch PHP Conference 2015 - The quest for global design principles

–The Stable dependencies principle

Depend in the direction of stability

Page 31: Dutch PHP Conference 2015 - The quest for global design principles

Stable thing Unstable thing

What to do when this happens?

Page 32: Dutch PHP Conference 2015 - The quest for global design principles

Also stable

Half stable thingStable thing

Unstable thing

Depend on something that you own

Page 33: Dutch PHP Conference 2015 - The quest for global design principles

–The Dependency inversion principle

Always depend on abstractions, not on concretions

Page 34: Dutch PHP Conference 2015 - The quest for global design principles

Dependency inversion for objects

• Depend on an interface instead of a class

• Separate a task from its implementation

Page 35: Dutch PHP Conference 2015 - The quest for global design principles

Stable thing

Communication between systems

External application (unstable)

Page 36: Dutch PHP Conference 2015 - The quest for global design principles

Mediator

Stable thing

Slightly better

External application (unstable)

Page 37: Dutch PHP Conference 2015 - The quest for global design principles

ConsumerApplication

Communication between systems

Message queue

External application (unstable)

Page 38: Dutch PHP Conference 2015 - The quest for global design principles

Dependency inversion for systems

• Depend on your own (messaging) system

• Communicate with other applications via a message queue

Page 39: Dutch PHP Conference 2015 - The quest for global design principles

Part IIInformation

I Stability

III Implementation

Page 40: Dutch PHP Conference 2015 - The quest for global design principles

Knowledge, data, duplication

Page 41: Dutch PHP Conference 2015 - The quest for global design principles

Nameplate order system

Nameplate machine

Business automation for creating nameplates

Page 42: Dutch PHP Conference 2015 - The quest for global design principles

<h1>You are about to order a nameplate!</h1>!!<p>Name on the plate: {{ name }}<br/>!Width of the plate: {{ 12*(name|length) }} cm.</p>!

Calculating the width of a nameplate

Knowledge

Page 43: Dutch PHP Conference 2015 - The quest for global design principles

class Nameplate!{!! private $name;!!! function __construct($name) {!! ! $this->name = $name;!! }!!! function widthInCm() {!! ! return strlen($this->name) * 12;!! }!}

Data object

Knowledge is close to the subject

Page 44: Dutch PHP Conference 2015 - The quest for global design principles

<h1>You are about to order a nameplate!</h1>!!<p>Name on the plate: {{ nameplate.name }}<br/>!Width of the plate: {{ nameplate.widthInCm) }} cm.</p>!

No knowledge in the template

Page 45: Dutch PHP Conference 2015 - The quest for global design principles

<nameplate>!! <name>Ibuildings</name>!</nameplate>

Serialized Nameplate object

Page 46: Dutch PHP Conference 2015 - The quest for global design principles

// calculate the width of the nameplate!!$nameplate = deserialize($xml);!$name = $nameplate['name'];!!$width = strlen($name);!!$widthPerCharacterInCm = 12;!!$widthInCm = $width * $widthPerCharacterInCm;!!// configure the nameplate machine ;)

Accepting messages

Duplication of knowledge

Page 47: Dutch PHP Conference 2015 - The quest for global design principles

<nameplate>!! <name>Buildings</name>!! <width>120</width>!</nameplate>

Deduplication

Knowledge is in one place, facts can be everywhere

Page 48: Dutch PHP Conference 2015 - The quest for global design principles

$nameplate = deserialize($xml);!!$width = $nameplate['width'];

Accepting messages

No duplicate knowledge anymore

Page 49: Dutch PHP Conference 2015 - The quest for global design principles

–The Don't repeat yourself principle

“Every piece of knowledge must have a single, unambiguous, authoritative representation

within a system.”

Even across applications!

Page 50: Dutch PHP Conference 2015 - The quest for global design principles

"Don't repeat yourself"

• Doesn't mean you can't repeat data

• It means you can't have knowledge in multiple locations

Page 51: Dutch PHP Conference 2015 - The quest for global design principles

Mutability

Page 52: Dutch PHP Conference 2015 - The quest for global design principles

What's the difference between...

class Money!{!! private $amount;!! private $currency;!!! public function setAmount($amount) {!! ! $this->amount = $amount;!! }!!! public function getAmount() {!! ! return $this->amount;!! }!!! ...!}

Page 53: Dutch PHP Conference 2015 - The quest for global design principles

... and this

class Money!{!! public $amount;!! public $currency;!}

Page 54: Dutch PHP Conference 2015 - The quest for global design principles

Inconsistent data$savings = new Money();!$savings->setAmount(1000);!!// what's the currency at this point?!!$savings->setCurrency('USD');!!// only now do we have consistent data!!$savings->setCurrency('EUR');!!// we have a lot more money now!!!$savings->setAmount('Amsterdam');

Page 55: Dutch PHP Conference 2015 - The quest for global design principles

Making something better of this

class Money!{!! private $amount;!! private $currency;!!! public function __construct($amount, $currency) {!! ! $this->setAmount($amount);!! }!!! private function setAmount($amount) {!! ! if (!is_int($amount) || $amount < 0) {!! ! ! throw new \InvalidArgumentException();!! ! }!!! ! $this->amount = $amount;!! }!}

Private

Required

Page 56: Dutch PHP Conference 2015 - The quest for global design principles

Immutability

• Once created, can not be modified

• Can only be replaced

Page 57: Dutch PHP Conference 2015 - The quest for global design principles

Consistent data!

$savings = new Money(1000, 'USD');!// we already have consistent data!!// we can't change anything anymore

Immutable data!

Page 58: Dutch PHP Conference 2015 - The quest for global design principles

Using immutable values

• Prevents bugs

• Prevents invalid state

Page 59: Dutch PHP Conference 2015 - The quest for global design principles

What about API messages?

<money>!! <amount>1000</amount>!! <currency>USD</currency>!</money>

PUT /savings/<money>!! <currency>EUR</currency>!</money>

POST /savings/

Page 60: Dutch PHP Conference 2015 - The quest for global design principles

Large object graphs<user>!! <first-name/>!! <last-name/> !! <mobile-phone-number/> !! <email-address/> !! <homepage/> !! <orders>!! ! <order/>!! ! ...!! </orders>!! <tickets>!! ! <ticket/>!! ! ...!! </tickets>!! <payments>!! ! <payment/>!! ! ...!! </payments>!</user>

Page 61: Dutch PHP Conference 2015 - The quest for global design principles

Forms & Doctrine ORM

$form = $this->createForm(new MoneyType());!$form->handleRequest($request);!!if ($form->isValid()) {!! $em = $this->getDoctrine()->getManager();!! $em->persist($form->getData());!!! // calculates change set and executes queries!!! $em->flush();!}

Page 62: Dutch PHP Conference 2015 - The quest for global design principles

If you allow your users to change every field at any time

• You end up with inconsistent data

• You loose the why of a change

• You end up with the what of only the last change

• You ignore the underlying real-world scenario

Page 63: Dutch PHP Conference 2015 - The quest for global design principles
Page 64: Dutch PHP Conference 2015 - The quest for global design principles

Commands and events

Register ConfirmRegistration

SendMessage

RegistrationConfirmed

MessageSent

UserRegisteredApplication

Page 65: Dutch PHP Conference 2015 - The quest for global design principles

Tomorrow!

Page 66: Dutch PHP Conference 2015 - The quest for global design principles

True

• Messages should serve actual use cases instead of patch operations

• After processing them, data should be in a valid state

Page 67: Dutch PHP Conference 2015 - The quest for global design principles

Part IIIImplementation

I StabilityII Information

IV Conclusion

Page 68: Dutch PHP Conference 2015 - The quest for global design principles

Implementation

Page 69: Dutch PHP Conference 2015 - The quest for global design principles

Leaking implementation details

Page 70: Dutch PHP Conference 2015 - The quest for global design principles

class Person!{!! /**!! * @return PhoneNumber[]!! */!! public function getPhoneNumbers() {!! ! return $this->phoneNumbers;!! }!}

Initial implementation

Page 71: Dutch PHP Conference 2015 - The quest for global design principles

class Person!{!! /**!! * @return ArrayCollection!! */!! public function getPhoneNumbers() {!! ! return $this->phoneNumbers;!! }!}

Implementation leakage (Doctrine)

Page 72: Dutch PHP Conference 2015 - The quest for global design principles

class Person!{!! /**!! * @return PhoneNumber[]!! */!! public function getPhoneNumbers() {!! ! return $this->phoneNumbers->toArray();!! }!}

Hiding implementation

Page 73: Dutch PHP Conference 2015 - The quest for global design principles

class NameplateController!{!! function getAction($id) {!! ! $nameplate = $this!! ! ! ->getDoctrine()!! ! ! ->getManager()!! ! ! ->getRepository(Nameplate::class)!! ! ! ->findOneBy(['id' => $id]);!!! ! if ($nameplate === null) {!! ! ! throw new NotFoundHttpException();!! ! }!! ! ...!! }!}

More implementation hiding

Actual field names!

null or false?

"find"?

Page 74: Dutch PHP Conference 2015 - The quest for global design principles

class NameplateRepository!{!! function fromId($id) {!! ! $nameplate = $this!! ! ! ->findOneBy(['id' => $id]);!!! ! if ($nameplate === null) {!! ! ! throw new NameplateNotFound($id);!! ! }!!! ! return $nameplate;!! }!}

Push it out of sight

Domain-specific exception

Hide specific return value

No "find"

Page 75: Dutch PHP Conference 2015 - The quest for global design principles

class NameplateController!{!! function getAction($id) {!! ! try {!! ! ! $nameplate = $this!! ! ! ! ->nameplateRepository!! ! ! ! ->fromId($id);!! ! } catch (NameplateNotFound $exception) {!! ! ! throw new NotFoundHttpException();!! ! }!!! ! ...!! }!}

Respect layers

Convert domain exception to web specific exception

Page 76: Dutch PHP Conference 2015 - The quest for global design principles

Plain old OOP

• Encapsulation

• Abstraction

Page 77: Dutch PHP Conference 2015 - The quest for global design principles

Limited by implementation details

Page 78: Dutch PHP Conference 2015 - The quest for global design principles

<ticket>! ...! <priority type="integer">1</priority>! ...

Assembla API

We do "switch ($priority)"

Page 79: Dutch PHP Conference 2015 - The quest for global design principles

<ticket>! ...! <priority key="highest">! <label>Highest</label>! </priority>! ...

Why not...

Page 80: Dutch PHP Conference 2015 - The quest for global design principles

<ticket>! ...! <is-story type="boolean">false</is-story>! <total-estimate type="float">0.0</total-estimate>! ...

Assembla API

Table "tickets" has a column "is_story"

Page 81: Dutch PHP Conference 2015 - The quest for global design principles

<story>! ...! <total-estimate type="float">0.0</total-estimate>! ...

Why not...

Page 82: Dutch PHP Conference 2015 - The quest for global design principles

Design your messages in such a way that

• You hide your implementation

• Clients won't need to reimplement your application

• Clients get the information they need

Page 83: Dutch PHP Conference 2015 - The quest for global design principles

API discovery

Page 84: Dutch PHP Conference 2015 - The quest for global design principles

/**! * @param string $password! * @param integer $algo! * @param array $options! */!function password_hash($password, $algo, array $options = array());

Undiscoverable API

What are my options here?

And here?

Page 85: Dutch PHP Conference 2015 - The quest for global design principles

class Algorithm extends \SplEnum!{!! const __default = self::BCRYPT;!!! const BCRYPT = 1;!}

Allow no mistakes

Page 86: Dutch PHP Conference 2015 - The quest for global design principles

[!! 'salt' => '...'!! 'cost' => 10!]

Options for bcrypt hashing

Page 87: Dutch PHP Conference 2015 - The quest for global design principles

class Bcrypt implements HashingStrategy!{!! /**! * @param integer $cost! * @param string|null $salt! */!! public function __construct($cost, $salt = null) {!! ! ...!! }!}

Inject a strategy

Page 88: Dutch PHP Conference 2015 - The quest for global design principles

function password_hash(!! $password, !! HashingStrategy $hashingStrategy!);

Inject a strategy

More explicit and... discoverable!

Page 89: Dutch PHP Conference 2015 - The quest for global design principles

Discoverability of an API

• Full Reflection capabilities :)

• Basic knowledge of English

Page 90: Dutch PHP Conference 2015 - The quest for global design principles

// I've got this password!$password = ...;!// I want to hash it...!// I found a function for this: password_hash()!password_hash($password, HashingStrategy $hashingStrategy);!// It requires an argument: a password (string)!// I already got a password right here:!password_hash($password);!// Wait, it requires a hashing strategy (a HashingStrategy object)!// I just found a class implementing that interface:!$hashingStrategy = new BcryptStrategy();!// That doesn't work, BcryptStrategy needs a cost!$hashingStrategy = new BcryptStrategy(10);!password_hash($password, $hashingStrategy);

Example of API discoveryWho is talking?

How stupid are they?How do you find out a valid range?

Page 91: Dutch PHP Conference 2015 - The quest for global design principles

/**! * @param array $options! */!function some_function(array $options);!!/**! * @param integer $type! */!function some_other_function($type);!!/**! * @param object $command! */!function handle($command);

Undiscoverable APIs

Page 92: Dutch PHP Conference 2015 - The quest for global design principles

Any kind of API should be maximally discoverable

Page 93: Dutch PHP Conference 2015 - The quest for global design principles

Everything should be an object

A class is a type

Page 94: Dutch PHP Conference 2015 - The quest for global design principles

Define lots of interfaces

An interface defines the public API of functions

Page 95: Dutch PHP Conference 2015 - The quest for global design principles

<?xml version="1.0" encoding="UTF-8"?>!<ticket>! <reporter>! <id>43</id>! <link rel="self" href="/api/reporters/43" />! <link rel="index" href="/api/reporters/" />! </reporter>! ...

HATEOAS

Links are a way to explain an "object"

Page 96: Dutch PHP Conference 2015 - The quest for global design principles

In summary

• Objects are just like applications

• Try to apply the same rules to them

Page 97: Dutch PHP Conference 2015 - The quest for global design principles

Think about

• Stable dependencies

• Duplication of facts, not knowledge

• Immutability over mutability

• No leakage of implementation details

• Everything should be maximally discoverable

Page 98: Dutch PHP Conference 2015 - The quest for global design principles

Questions? Feedback?

joind.in/14217 Thanks!


Recommended