Download - Domain Driven Design

Transcript
Page 1: Domain Driven Design

DOMAIN DRIVEN DESIGN

TACKLING COMPLEXITY

Page 2: Domain Driven Design

@pascallarocque

○ TRUSTCHARGE TEAM

○ BEHAT GUY

○ TDD GUY

○ SOLID GUY

○ PATTERN GUY

○ FATHER OF 3

○ STAR WARS GEEK

Page 3: Domain Driven Design

SOFTWARE IS COMPLICATED

Page 4: Domain Driven Design

CURRENT ARCHITECTURE

CONTROLLER

ORM

DATABASE

APPLICATION

DATA ACCESS /

BUSINESS OBJECT/

PERSISTENCE

DATA STORE

BZ

● BUSINESS LOGIC IN

CONTROLLER AND IN DATA

ACCESS OBJECTS

● FRAMEWORK COUPLED TO

CONTROLLER

● DIRECT ACCESS TO DATA

OBJECT FROM CONTROLLER

Page 5: Domain Driven Design

PROBLEM

○ DEVELOPERS / ARCHITECTS ARE ONLY THINKING ABOUT THE

FRAMEWORK (DB, ORM, CACHING)

○ MOST OF OUR DEVELOPMENT TIME IS SPENT WRITING PLUMPING

FOR THE FRAMWORK INSTEAD OF REAL BUSINESS LOGIC

○ THE MEANING OF OOP IS LOST

Page 6: Domain Driven Design

WHAT

○ DOMAIN DRIVEN DESIGN IS ABOUT MAPPING BUSINESS DOMAIN

CONCEPT INTO CODE

WHY

○ TO CREATE SOFTWARE THAT REFLECT THE BUSINESS RATHER

THAN THE FRAMEWORK

DOMAIN DRIVEN DESIGN

Page 7: Domain Driven Design

DOMAIN DRIVEN ARCHITECTURE

CONTROLLER

SERVICE

DOMAIN

DAO

DATABASE

FRAMEWORK

APPLICATION

DOMAIN

DATA ACCESS /

PERSISTENCE

DATA STORE

● HTTP

● SESSION MANAGEMENT

● RPC

● PERSISTENCE

● CACHING

● SECURITY

● MESSAGING

● ALL LAYERS SUPPORT POPO BASED DESIGN

● CONTROLLERS AND SERVICES ARE

CONSUMERS OF DOMAIN OBJECTS

● BUSINESS LOGIC ONLY IN DOMAIN OBJECTS

● NO DIRECT ACCESS TO DAO EXCEPT FROM

DOMAIN OBJECT

● DOMAIN FIRST, FRAMEWORK SECOND

● FRAMEWORK CONCERNS ARE

IMPLEMENTED BY DI

Page 8: Domain Driven Design

ADVANTAGES

○ PROMOTES HIGH COHESION AND LOW COUPLING

○ EASY TO TEST DOMAIN COMPONENTS

○ BUSINESS (DOMAIN) LOGIC IS ISOLATED FROM NON-DOMAIN AND

FRAMEWORK CODE

○ ADDING / CHANGING SERVICES DOES NOT INFLUENCE THE

DOMAIN OR OTHER SERVICES

Page 9: Domain Driven Design

DEVELOPMENT IMPACT

EFFORT TO

ENHANCE /

MAINTAIN

COMPLEXITY TO IMPLEMENT

SOURCE: PATTERNS OF ENTERPRISE APPLICATION ARCHITECTURE, MARTIN FOWLER

TRANSACTION

SCRIPTS

TABLE MODULES

DOMAIN MODEL

Page 10: Domain Driven Design

HOW TO DO DDD

THE UBIQUITOUS LANGUAGE

Page 11: Domain Driven Design

UBIQUITOUS LANGUAGE

○ SHARED TEAM LANGUAGE (DEVELOPERS AND DOMAIN EXPERTS)

○ UBIQUITOUS IS NOT AN ATTEMPT TO DESCRIBE ENTERPRISE-WIDE

DOMAIN LANGUAGE

○ ONE UBIQUITOUS LANGUAGE PER BOUNDED CONTEXT (CODE

BASE)

○ IF YOU TRY TO APPLY A SINGLE UBIQUITOUS LANGUAGE TO AN

ENTIRE ENTERPRISE, YOU WILL FAIL

Page 12: Domain Driven Design

public function chargeCustomer(ChargecodeData $chargecode, Transaction $transaction) {

if($chargecode->getEmail() === $transaction->getCustomerEmail()

&& $transaction->getCustomerCreditCardExpiration > date(‘Y-m’)

&& in_array($transaction->getStatus(), [‘SALE’, ‘REBILL’, ‘AUTHORISE’])

&& $chargecode->isUsed() === false) {

// Do charge

}

throw new ChargeCustomerException();

}

/**

* @Inject

* @var ChargeCodeValidationPolicy

*/

protected $oneClickPolicy;

public function chargeCustomer(ChargecodeData $chargecode, Transaction $transaction) {

if($this->oneClickPolicy->isAllowed($chargecode, $transaction)) {

// Do charge

}

throw new ChargeCustomerException();

}

Page 13: Domain Driven Design

DOMAIN OBJECTS ARE INSTANCES OF REAL ENTITIES THAT HOLD THE

BUSINESS LOGIC.

DOMAIN OBJECTS

Page 14: Domain Driven Design

MAIN ELEMENTS OF DDD

Page 15: Domain Driven Design

○ DESIGN A CONCEPT AS AN ENTITY WHEN YOU CARE ABOUT ITS

INDIVIDUALITY, WHEN DISTINGUISHING IT FROM ALL OTHER

OBJECTS IN A SYSTEM IS A MANDATORY CONSTRAINT

(CUSTOMER, MEMBERSHIP)

○ THE ENTITY SHOULD NOT BE BOUND TO ANY FRAMEWORK (ORM),

IT SHOULD BE A PLAIN OLD PHP OBJECT (POPO)

ENTITIES

Page 16: Domain Driven Design

/** @Entity */class Membership{

/** @Id @Column(type="integer") @GeneratedValue */

private $id;

/** @Column(type="string") */

private $status;

/** @ManyToOne(targetEntity="Customer") */

private $customer;

/** @OneToMany(targetEntity="Transaction", mappedBy="membership") */

private $transactions;

public function __construct {

$this->transactions = new ArrayCollection();

}

public function getCustomer() { return $this->customer; }

public function getTransactions() { return $this->transactions;}}POPO

Page 17: Domain Driven Design

VALUE OBJECT

○ STRIVE TO MODEL USING VALUE OBJECTS INSTEAD OF ENTITIES

WHEREVER POSSIBLE

○ IMMUTABLE, AFTER THE OBJECT HAS BEEN INSTANTIATED, NONE

OF ITS METHODS WILL CAUSE ITS STATE TO CHANGE

○ INSTEAD OF CHANGING THE ATTRIBUTES, WOULD OBJECT

REPLACEMENT WORK INSTEAD?

Page 18: Domain Driven Design

$factory = new ChargeCodeGenerationDataFactory();

$chargeCodeData = $factory->generateFromArray($data);

class ChargeCodeGenerationData{

private $transactionId;

private $emailAddress;

private $accountId;

public function __construct($transactionId, $emailAddress, $accountId) {

$this->transactionId = $transactionId;

$this->emailAddress = $emailAddress;

$this->accountId = $accountId;

}

public function toArray() { return [‘transactionId’ => $this->transactionId,

‘emailAddress’ => $this->emailAddress,

‘accountId’ => $this->accountId]; }

public function toJSON() { return json_encode($this->toArray());}}

Page 19: Domain Driven Design

○ IN A CUSTOMER MANAGEMENT CONTEXT CUSTOMER SHOULD BE

AN ENTITY

○ IN A MEMBERSHIP CONTEXT CUSTOMER SHOULD BE A VALUE

OBJECT

VO BASED ON BOUNDED CONTEXT

Page 20: Domain Driven Design

○ PROVIDES FUNCTIONALITIES FOR THE DOMAIN

○ STATELESS

○ DOMAIN SERVICES != APPLICATION SERVICES != CONTROLLER

○ DOMAIN SERVICES CAN HOST DOMAIN LOGIC

○ PERFORM A SIGNIFICANT BUSINESS PROCESS

○ TRANSFORM A DOMAIN OBJECT FROM ONE COMPOSITION TO ANOTHER

○ CALCULATE A VALUE REQUIRING INPUT FROM MORE THAN ONE DOMAIN OBJECT

SERVICES

Page 21: Domain Driven Design

class OneClickService{

/**

* @var ChargecodeAuthcodeValidatorInterface

*/

protected $_dataAccessor; /**

* @var \Tc_Bz_HashGenerator_Interface

*/

protected $_hashGenerator;

/**

* @var ChargecodeAuthcodeValidationResponseDataFactoryInterface

*/

protected $_factoryResponceValidate;

public function __construct($dataAccessor, $hashGenerator, $factory) { $this-

>_dataAccessor = $dataAccessor;

$this->_hashGenerator = $hashGenerator;

$this->_factoryResponceValidate = $factory;

}

/**

* validate chargecode By Authcode

*

* @param ChargecodeAuthcodeDataInterface $chargecodeAuthcodeValidationData

* @return ChargecodeAuthcodeValidationResponseData

* @throws ChargecodeAuthcodeValidationDataException

*/

public function validateChargecodeByAuthcode(ChargecodeAuthcodeDataInterface $data)

{

$decryptedData = $this->_hashGenerator->decipher( $data>getCryptedString());

if ($decryptedData === false) {

throw new ChargecodeAuthcodeValidationDataException('Not decipherable');

}

$this->_validateEmailLinkedToAuthcode($data->getEmailAddress(),

$data->getTransactionId());

$this->_validateCustomCodeIdLinkedToEnterprise($data->getAccountIdDestination(),

$data->getEnterpriseId());

$this->_validateCustomerIs1Clickable($data->getTransactionId());

$this->_validateCodeNotUsed($data->getAccountIdDestination(),

$data->getEmailAddress());

$reponseData = $data->toArray();

$reponseData['chargecode'] = $decryptedData['hash'];

$response = $this->_factoryResponseValidate->generateResponse($reponseData);

return $response;

}

}

Page 22: Domain Driven Design

○ GROUP OF ASSOCIATED ENTITIES AND VALUE OBJECTS TREATED

AS A UNIT FOR THE PURPOSE OF DATA EXCHANGE

○ ENTITY AS ROOT ELEMENT

○ ONLY THE ROOT IS OBTAINED THROUGH QUERIES

○ THE ENTITY IS RESPONSIBLE FOR MAINTAINING THE INVARIANCE

○ DELETE OPERATION MUST REMOVE EVERYTHING WITHIN THE

AGGREGATE BOUNDARY AT ONCE (CASCADE DELETE)

AGGREGATES

Page 23: Domain Driven Design

AGGREGATE

MEMBERSHIP

CUSTOMER

CREDIT CARD EMAILTRANSACTION

EMAILCREDIT CARDTRANSACTION

TRANSACTION

SITE

Page 24: Domain Driven Design

○ PROVIDES ENCAPSULATION FOR OBJECT / AGGREGATE CREATION

○ PRODUCES AN OBJECT IN A CONSISTENT STATE

FACTORIES

Page 25: Domain Driven Design

class ChargecodeAuthcodeGenerationResponseDataFactory

{

/**

* Factory method to generate chargecode validation data by authcode

*

* @param array $data Data used to generate

* @throws ChargecodeAuthcodeValidationDataException

* @return ChargecodeAuthcodeGenerationResponseData

*/

public function generateFromArray(array $data)

{

$this->_validateParameters($data);

$chargecodeData = $this->_generateDataAccessObject($data);

$data = $this->_unsetUnusedParameters($data);

$chargecodeData->setParams($data);

return $chargecodeData;

}

protected function _sendException()

{

throw new ChargecodeAuthcodeGenerationResponseDataException('Could not Generate a response');

}

protected function _generateDataAccessObject(array $data)

{

return new ChargecodeAuthcodeGenerationResponseData($data['authCode'], $data['account_id_destination'], $data['email_address'],

$data['crypted_string'], null);

}

}

Page 26: Domain Driven Design

○ PATTERN FOR RETRIEVING AND SAVING OBJECTS IN THE DB

○ SHOULD NOT BE TIED TO SPECIFIC FRAMEWORK (ORM)

○ EASY SUBSTITUTION FOR TESTING

REPOSITORIES

Page 27: Domain Driven Design

class SubEnterpriseRepository

{

/**

* @Inject

* @var SubEnterpriseDataAccessorInterface

*/

private $_dataAccessor;

/**

* @Inject

* @var SubEnterpriseParserInterface

*/

private $_dataParsor;

/**

* @Inject

* @var SubEnterpriseFactoryInterface

*/

private $_dataFactory;

/**

* @param $account

* @return mixed

*/

public function findSubEnterpriseByAccount(Account $account)

{

$results = $this->_dataAccessor->findSubEnterpriseByAccount($account);

$parsedResults = $this->_dataParsor->parseResults($results);

return $this->_dataFactory->create($parsedResults);

}

}

Page 28: Domain Driven Design

○ OBJECTS SHOULD NOT DEPEND ON CONCRETE CONSTRUCTOR

VARIABLES, INSTEAD TO SHOULD USE INTERFACES

○ OBJECTS SHOULD NOT HAVE TO CONFIGURE ITS INSTANCE

VARIABLES IN THE CONSTRUCTOR OR INIT FUNCTION, INSTEAD

THEY SHOULD RECEIVE THEM ALREADY PRE-CONFIGURED

DEPENDENCY INJECTION

Page 29: Domain Driven Design

"Dependency Injection" is a 25-dollar

term for a 5-cent concept. [...]

Dependency injection means giving

an object its instance variables. [...].

- James Shore

Page 30: Domain Driven Design

PHP-DI

class SubEnterpriseRepository

{

/**

* @Inject

* @var SubEnterpriseDataAccessorInterface

*/

private $_dataAccessor;

/**

* @Inject

* @var SubEnterpriseParserInterface

*/

private $_dataParsor;

/**

* @Inject

* @var SubEnterpriseFactoryInterface

*/

private $_dataFactory;

/**

* @param $account

* @return mixed

*/

public function findSubEnterpriseByAccount(Account $account)

{

$results = $this->_dataAccessor->findSubEnterpriseByAccount($account);

$parsedResults = $this->_dataParsor->parseResults($results);

return $this->_dataFactory->create($parsedResults);

}

}

// Load the container

$container = new DI\Container();

$container->addDefinitionsByFile(new ArrayDefinitionFile(‘di.php’));

// Create the object

$repository = new SubEnterpriseRepository();

// Inject the dependencies

$container->injectOn($repository);

// di.php

return [

‘SubEnterpriseDataAccessorInterface’

=> [ ‘class’ : ‘DoctrineSubEnterpriseAccessor’,

‘methods’ => [

‘setHydrator’ => DOCTRINE_CORE::HYDRATE_SCALAR

]

],

‘SubEnterpriseParserInterface’

=> new SubEnterpriseDoctrineToArrayParser(),

‘SubEnterpriseFactoryInterface’

=> new SubEnterpriseResultFactory()

];

Page 31: Domain Driven Design

PHP-DI-ZF1

/**

* Initialize the dependency injection container

*/

protected function _initDependencyInjection()

{

$this->bootstrap('DependencyInjectionContainerResource');

$container = $this->getResource('DependencyInjectionContainerResource');

$dispatcher = new \DI\ZendFramework1\Dispatcher();

$dispatcher->setContainer($container);

$frontController = Zend_Controller_Front::getInstance();

$frontController->setDispatcher($dispatcher);

}

class Tc_Application_Resource_DependencyInjectionContainerResource extends

Zend_Application_Resource_ResourceAbstract

{

public function init()

{

$this->_container = new \DI\Container();

foreach($this->_definitionFilePath as $DIResourceFile) {

$file = $this->_loadDefinitionFile(realpath($DIResourceFile));

$this->_container->addDefinitionsFromFile($file);

}

return $this->_container;

}

private function _loadDefinitionFile($DIResourceFile)

{

$file = null;

if (0 === substr_compare($DIResourceFile, 'php', -3, 3, true)) {

$file = new \DI\Definition\FileLoader\ArrayDefinitionFileLoader($DIResourceFile);

}

if (0 === substr_compare($DIResourceFile, 'yml', -3, 3, true)) {

$file = new \DI\Definition\FileLoader\YamlDefinitionFileLoader($DIResourceFile);

}

if (0 === substr_compare($DIResourceFile, 'json', -4, 4, true)) {

$file = new \DI\Definition\FileLoader\JsonDefinitionFileLoader($DIResourceFile);

}

if($file === null) {

throw new Gamma_Application_Resource_Exception('Invalid Definition File Type');

}

return $file;

}

Page 32: Domain Driven Design

PHP-DI-ZF1

class Direct_FollowController extends Zend_Controller_Action

{

/**

* @Inject(lazy=true)

* @var \Tc\Service\ChargeCodeService

*/

private $_oneClickService;

/**

* @Inject(lazy=true)

* @var \Tc\ChargeCode\Data\ChargecodeAuthcodeGenerationDataFactory

*/

private $_factory;

public function generateChargeCodeByAuthcodeAction()

{

$request = $this->getRequest();

$this->getResponse()->setHeader('Content-Type', 'application/json', true);

try {

$chargeCodeGenerationData = $this->_factory->generate($request->getParams());

$this->view->answer = $this->_oneClickService->generate($chargeCodeGenerationData);

$this->render('generate-charge-code');

} catch (\Tc\ChargeCode\Data\Exception\ChargeCodeGenerationDataException $chargeCodeException) {

$this->view->requiredParameters = $chargeCodeException;

$this->render('charge-code-generation-authcode-invalid-parameters');

} catch (\Tc\ChargeCode\Data\Exception\ChargecodeAuthcodeGenerationResponseDataException $chargeCodeException) {

$this->view->requiredParameters = $chargeCodeException;

$this->render('charge-code-generation-authcode-invalid-parameters');

}

}

Page 33: Domain Driven Design

DOMAIN & SUB-DOMAIN

THE HEART OF DDD

Page 34: Domain Driven Design

DOMAIN vs DOMAIN MODEL

○ THE DOMAIN IS THE PROBLEM TO BE ADDRESSED IN SOFTWARE

○ A DOMAIN MODEL IS THE REPRESENTATION OF IN CODE OF THE

SOLUTION FOR THE DOMAIN PROBLEM

○ HAS TO BE CREATED WITH THE COOPERATION OF DEVELOPERS

AND DOMAIN EXPERTS

○ THE GOAL OF DOMAIN DRIVEN DESIGN IS TO CREATE OBJECT IN

CODE THAT REFLECT THE DOMAIN

Page 35: Domain Driven Design

○ DOMAIN CAN BE DECOMPOSED INTO SUB-DOMAINS (PRODUCTS,

BILLING, MEMBERSHIP)

○ SUB-DOMAIN SPLIT THE DOMAIN INTO DIFFERENT UNIQUE

SECTIONS

○ BOUNDED CONTEXT SPLIT THE CODE INTO DIFFERENT CODE

BASES

○ SUB-DOMAIN CAN BE IMPLEMENTED BY MULTIPLE BOUNDED

CONTEXTS (MEMBERSHIP AND MEMBERSHIP REBILL)

SUB-DOMAIN vs BOUNDED CONTEXT

Page 36: Domain Driven Design

ORGANIZATIONS WHICH DESIGN

SYSTEMS ARE CONSTRAINED TO

PRODUCE DESIGNS WHICH ARE

COPIES OF THE COMMUNICATION

STRUCTURES OF THESE

ORGANIZATIONS- Melvin Conway

Page 37: Domain Driven Design

SUB-DOMAIN BOUNDARIES ARE DETERMINED IN PART BY THE

COMMUNICATION STRUCTURES WITHIN AN ORGANIZATION

CONWAY’S LAW

Page 38: Domain Driven Design
Page 39: Domain Driven Design

○ CODE BASE FOR DOMAIN MODEL CONTEXT

○ EVERY MODEL’S PROPERTIES AND OPERATIONS HAS SPECIAL

MEANING WITHIN THE SPECIFIC CONTEXT

BOUNDED CONTEXTS

Page 40: Domain Driven Design

ENTITYVALUE OBJECT

Page 41: Domain Driven Design

CONTEXT MAPPING

○ PARTNERSHIP○ SUCCEED OR FAIL TOGETHER

○ COORDINATED PLANNING

○ JOINT MANAGEMENT OF

INTEGRATION

○ SCHEDULED COMPLETION

○ SHARED KERNEL○ INTIMATE INTERDEPENDENCIES

○ KEEP IT SMALL

○ CAN’T BE CHANGED WITHOUT

CONSULTATION

○ CUSTOMER-SUPPLIER○ UPSTREAM / DOWNSTREAM

RELATIONSHIP

○ DOWNSTREAM PRIORITIES FACTOR

INTO UPSTREAM PLANNING

○ NEGOTIATED SCHEDULE

○ CONFORMIST○ UPSTREAM / DOWNSTREAM

RELATIONSHIP

○ UPSTREAM HAS NO MOTIVATION TO

PROVIDE FOR DOWNSTREAM

Page 42: Domain Driven Design

CONTEXT MAPPING

○ ANTICORRUPTION LAYER○ TRANSLATION LAYER

○ LAYER TRANSLATES IN ONE OR BOTH

DIRECTIONS BETWEEN THE TWO

MODELS

○ OPEN HOST SERVICE○ SOA

○ PROTOCOL TO GIVE ACCESS TO

YOUR SUBSYSTEM

○ PUBLISHED LANGUAGE○ WELL-DOCUMENTED SHARED

LANGUAGE

○ SEPARATE WAYS○ COMPLETELY CUT LOOSE FROM

EACH OTHER

○ INTEGRATION IS EXPENSIVE WITH

SMALL BENEFITS

○ BIG BALL OF MUD○ MIXED MODELS

○ INCONSISTENT BOUNDARIES

○ DRAW A BOUNDARY AROUND THE

MESS

○ DO NOT TRY TO APPLY

SOPHISTICATED MODELING

Page 43: Domain Driven Design

Any 3rd party system

that I have to integrate

with, was written by

a drunken monkey

typing with his feet

- Oren Eini

Page 44: Domain Driven Design

SUB-DOMAIN

SUB-DOMAIN

ANTICORRUPTION

LAYER THAT

TRANSLATES

USER/ROLES

BETWEEN SUB-

DOMAINS

Page 45: Domain Driven Design

Any fool can write code that a

computer can understand. Good

programmers write code that humans

can understand.

- Martin Fowler

Page 46: Domain Driven Design

REFERENCES

DOMAIN-DRIVEN DESIGN

BY ERIC EVANS

IMPLEMENTING DOMAIN-DRIVEN

DESIGN

BY VAUGHN VERNON