99
Global Design Principles The Quest for Matthias Noback

The quest for global design principles - PHP Benelux 2016

Embed Size (px)

Citation preview

Page 1: The quest for global design principles - PHP Benelux 2016

Global Design

Principles

The Quest for

Matthias Noback

Page 2: The quest for global design principles - PHP Benelux 2016

Stability

Change

Page 3: The quest for global design principles - PHP Benelux 2016

Stability versus change

• Backwards compatibility of APIs

• Semantic versioning

• HTTP status codes

• XML schemas

Page 4: The quest for global design principles - PHP Benelux 2016

For internal APIs

only!

Page 5: The quest for global design principles - PHP Benelux 2016
Page 6: The quest for global design principles - PHP Benelux 2016

The key is in the concept of communication

ReceiverSender

Page 7: The quest for global design principles - PHP Benelux 2016

What's being communicated?

Message

Type ("BookHotel")

Value (“Ter Elst", “29-01-2016“, “30-01-2016”)

Page 8: The quest for global design principles - PHP Benelux 2016

Message flavours

Command Event

Imperative "BookHotel"

Informational "HotelWasBooked"

Page 9: The quest for global design principles - PHP Benelux 2016

Message flavours

Query Document

Interrogatory "GetRecentBookings"

Neutral "BookingsCollection"

Page 10: The quest for global design principles - PHP Benelux 2016

Sender

Construct the message

Message

Translate

Construct

(Prepare fortransport)

Page 11: The quest for global design principles - PHP Benelux 2016

Receiver

Deconstruct the message

(Unwrap)

Translate

Construct

Page 12: The quest for global design principles - PHP Benelux 2016

Global design principles can be

discovered

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

Page 13: The quest for global design principles - PHP Benelux 2016

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: The quest for global design principles - PHP Benelux 2016

class AccountService{

public function deposit($accountId, Money $amount) {

...}

}Opportunity for

sending a message

Inter-object communication

Page 15: The quest for global design principles - PHP Benelux 2016

$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: The quest for global design principles - PHP Benelux 2016

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: The quest for global design principles - PHP Benelux 2016

PUT /accounts/deposit/ HTTP/1.1Host: localhost

{"accountId": "123","currency": "EUR","amount": 20000

}

Inter-application communication

Opportunity for sending a message

Page 18: The quest for global design principles - PHP Benelux 2016

$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: The quest for global design principles - PHP Benelux 2016

/** @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: The quest for global design principles - PHP Benelux 2016

Global design principles

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

Page 21: The quest for global design principles - PHP Benelux 2016

Part IStability

III Implementation

II Information

Page 22: The quest for global design principles - PHP Benelux 2016

Sender Receiver

Communication

Dependency relation!

Page 23: The quest for global design principles - PHP Benelux 2016

Stable Unstable

What is safer?

Unstable Stable

OR

Page 24: The quest for global design principles - PHP Benelux 2016

To be stable"Steady in position or balance;

firm"

Page 25: The quest for global design principles - PHP Benelux 2016

Irresponsible

What makes something unstable?

1. Nothing depends on it

It doesn't need to stay the same for anyone

Page 26: The quest for global design principles - PHP Benelux 2016

Dependent

What makes something unstable?

2. It depends on many things

Any of the dependencies could change at any time

Page 27: The quest for global design principles - PHP Benelux 2016

Stability is not a quality of one thing

It emerges from the environment of that thing

Page 28: The quest for global design principles - PHP Benelux 2016

Responsible

What makes something stable?

1. Many things depend on it

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

Page 29: The quest for global design principles - PHP Benelux 2016

Independent

What makes something stable?

2. It depends on nothing

It is not affected by changes in anything

Page 30: The quest for global design principles - PHP Benelux 2016

–The Stable dependencies principle

Depend in the direction of stability

Page 31: The quest for global design principles - PHP Benelux 2016

Stable package Unstable package

What to do when this happens?

Page 32: The quest for global design principles - PHP Benelux 2016

Stable package

Depend on something that you own

Unstable packageStable package

Page 33: The quest for global design principles - PHP Benelux 2016

–The Dependency inversion principle

Always depend on abstractions, not on concretions

Page 34: The quest for global design principles - PHP Benelux 2016

Dependency inversion for objects

• Depend on an interface instead of a class

• Separate a task from its implementation

Page 35: The quest for global design principles - PHP Benelux 2016

Stable thing

Communication between systems

External application (unstable)

Page 36: The quest for global design principles - PHP Benelux 2016

Mediator

Stable thing

Slightly better

External application (unstable)

Page 37: The quest for global design principles - PHP Benelux 2016

ConsumerApplication

Communication between systems

Message queue

External application (unstable)

Page 38: The quest for global design principles - PHP Benelux 2016

Dependency inversion for systems

• Depend on your own (messaging) system

• Communicate with other applications through a message queue

Page 39: The quest for global design principles - PHP Benelux 2016

Part IIInformation

I Stability

III Implementation

Page 40: The quest for global design principles - PHP Benelux 2016

Knowledge, data, duplication

Page 41: The quest for global design principles - PHP Benelux 2016

Nameplate order system

Nameplate machine

Business automation for creating nameplates

Page 42: The quest for global design principles - PHP Benelux 2016

<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: The quest for global design principles - PHP Benelux 2016

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: The quest for global design principles - PHP Benelux 2016

<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: The quest for global design principles - PHP Benelux 2016

<nameplate><name>Ibuildings</name>

</nameplate>

Serialized Nameplate object

Page 46: The quest for global design principles - PHP Benelux 2016

// 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: The quest for global design principles - PHP Benelux 2016

<nameplate><name>Ibuildings</name><width>120</width>

</nameplate>

Deduplication

Knowledge is in one place, facts can be everywhere

Page 48: The quest for global design principles - PHP Benelux 2016

$nameplate = deserialize($xml);

$width = $nameplate['width'];

Accepting messages

No duplicate knowledge anymore

Page 49: The quest for global design principles - PHP Benelux 2016

–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: The quest for global design principles - PHP Benelux 2016

"Don't repeat yourself"

• Doesn't mean you can't repeat data

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

Page 51: The quest for global design principles - PHP Benelux 2016

Mutability

Page 52: The quest for global design principles - PHP Benelux 2016

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: The quest for global design principles - PHP Benelux 2016

... and this

class Money{

public $amount;public $currency;

}

Page 54: The quest for global design principles - PHP Benelux 2016

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: The quest for global design principles - PHP Benelux 2016

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: The quest for global design principles - PHP Benelux 2016

Immutability

• Once created, can not be modified

• Can only be replaced

Page 57: The quest for global design principles - PHP Benelux 2016

Consistent data!

$savings = new Money(1000, 'USD');// we already have consistent data

// we can't change anything anymore

Immutable data!

Page 58: The quest for global design principles - PHP Benelux 2016

Using immutable values

• Prevents bugs

• Prevents invalid state

Page 59: The quest for global design principles - PHP Benelux 2016

What about API messages?

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

</money>

PUT /savings/<money>

<currency>EUR</currency></money>

POST /savings/

Page 60: The quest for global design principles - PHP Benelux 2016

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: The quest for global design principles - PHP Benelux 2016

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: The quest for global design principles - PHP Benelux 2016

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: The quest for global design principles - PHP Benelux 2016
Page 64: The quest for global design principles - PHP Benelux 2016

Commands and events

Register ConfirmRegistration

SendMessage

RegistrationConfirmed

MessageSent

UserRegisteredApplication

Page 65: The quest for global design principles - PHP Benelux 2016
Page 66: The quest for global design principles - PHP Benelux 2016

True

• Messages should serve actual use cases instead of patch operations

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

Page 67: The quest for global design principles - PHP Benelux 2016

Part IIIImplementation

I StabilityII Information

IV Conclusion

Page 68: The quest for global design principles - PHP Benelux 2016

Implementation

Page 69: The quest for global design principles - PHP Benelux 2016

Leaking implementation details

Page 70: The quest for global design principles - PHP Benelux 2016

class Person{

/** * @return PhoneNumber[] */public function getPhoneNumbers() {

return $this->phoneNumbers;}

}

Initial implementation

Page 71: The quest for global design principles - PHP Benelux 2016

class Person{

/** * @return ArrayCollection */public function getPhoneNumbers() {

return $this->phoneNumbers;}

}

Implementation leakage (Doctrine)

Page 72: The quest for global design principles - PHP Benelux 2016

class Person{

/** * @return PhoneNumber[] */public function getPhoneNumbers() {

return $this->phoneNumbers->toArray();}

}

Hiding implementation

Page 73: The quest for global design principles - PHP Benelux 2016

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: The quest for global design principles - PHP Benelux 2016

class NameplateRepository{

function byId($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: The quest for global design principles - PHP Benelux 2016

class NameplateController{

function getAction($id) {try {

$nameplate = $this->nameplateRepository->byId($id);

} catch (NameplateNotFound $exception) {throw new NotFoundHttpException();

}

...}

}

Respect layers

Convert domain exception to web specific exception

Page 76: The quest for global design principles - PHP Benelux 2016

Basically just plain old OOP

• Encapsulation

• Abstraction

Page 77: The quest for global design principles - PHP Benelux 2016

Implementation details and usability

Page 78: The quest for global design principles - PHP Benelux 2016

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

Assembla API

Page 79: The quest for global design principles - PHP Benelux 2016

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

Why not...

Page 80: The quest for global design principles - PHP Benelux 2016

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

Assembla API

Page 81: The quest for global design principles - PHP Benelux 2016

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

Why not...

Page 82: The quest for global design principles - PHP Benelux 2016

Design your messages in such a way that

• You hide your implementation

• Clients won't need to reimplement your logic

• Clients get the information they need

Page 83: The quest for global design principles - PHP Benelux 2016

API discovery

Page 84: The quest for global design principles - PHP Benelux 2016

/** * @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: The quest for global design principles - PHP Benelux 2016

class Algorithm extends \SplEnum{

const __default = self::BCRYPT;

const BCRYPT = 1;}

Allow no mistakes

Page 86: The quest for global design principles - PHP Benelux 2016

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

]

Options for bcrypt hashing

Page 87: The quest for global design principles - PHP Benelux 2016

class Bcrypt implements HashingStrategy{

/** * @param integer $cost * @param string|null $salt */

public function __construct($cost, $salt = null) {...

}}

Inject a strategy

Page 88: The quest for global design principles - PHP Benelux 2016

function password_hash($password, HashingStrategy $hashingStrategy

);

Inject a strategy

More explicit and... discoverable!

Page 89: The quest for global design principles - PHP Benelux 2016

Discoverability of an API

• Full Reflection capabilities :)

• Basic knowledge of English

Page 90: The quest for global design principles - PHP Benelux 2016

// I've got this password I want to hash…$password = ...;

// Look, 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: The quest for global design principles - PHP Benelux 2016

/** * @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: The quest for global design principles - PHP Benelux 2016

Any kind of API should be maximally discoverable

Page 93: The quest for global design principles - PHP Benelux 2016

Everything should be an object

A class is a type

Page 94: The quest for global design principles - PHP Benelux 2016

Define lots of interfaces

An interface defines the public API of functions

Page 95: The quest for global design principles - PHP Benelux 2016

<?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: The quest for global design principles - PHP Benelux 2016

Summary/Hypothesis

• Objects are just like applications

• Try to apply the same rules to them

Page 97: The quest for global design principles - PHP Benelux 2016

Think about

• Stable dependencies

• Duplication of facts, not knowledge

• Immutability over mutability

• No leakage of implementation details

• Everything should be maximally discoverable

Page 98: The quest for global design principles - PHP Benelux 2016

–Chris Hadfield

“… I should do things that keep me moving in the right direction, just in case — and I should be sure

those things interest me, so whatever happens, I’m happy.”

Page 99: The quest for global design principles - PHP Benelux 2016

Questions? Feedback?

joind.in/talk/f3b34 Thanks!