Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Domain Driven Design

2,102 views

Published on

Software is complicated, trying to represent the business logic in code requires allot of communication between the programmers and the domain experts. Domain Driven Design provided methods to facilitate this process

Published in: Technology
  • Be the first to comment

Domain Driven Design

  1. 1. DOMAIN DRIVEN DESIGN TACKLING COMPLEXITY
  2. 2. @pascallarocque ○ TRUSTCHARGE TEAM ○ BEHAT GUY ○ TDD GUY ○ SOLID GUY ○ PATTERN GUY ○ FATHER OF 3 ○ STAR WARS GEEK
  3. 3. SOFTWARE IS COMPLICATED
  4. 4. CURRENT ARCHITECTURE APPLICATION CONTROLLER DATA ACCESS / BUSINESS OBJECT/ PERSISTENCE BZ ORM ● ● ● DATA STORE DATABASE BUSINESS LOGIC IN CONTROLLER AND IN DATA ACCESS OBJECTS FRAMEWORK COUPLED TO CONTROLLER DIRECT ACCESS TO DATA OBJECT FROM CONTROLLER
  5. 5. 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
  6. 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
  7. 7. DOMAIN DRIVEN ARCHITECTURE CONTROLLER APPLICATION SERVICE FRAMEWORK ● ● ● ● ● ● ● HTTP SESSION MANAGEMENT RPC PERSISTENCE CACHING SECURITY MESSAGING DOMAIN DOMAIN ● ● ● ● DATA ACCESS / PERSISTENCE DATA STORE DAO DATABASE ● ● 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
  8. 8. 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
  9. 9. DEVELOPMENT IMPACT TRANSACTION SCRIPTS TABLE MODULES DOMAIN MODEL EFFORT TO ENHANCE / MAINTAIN COMPLEXITY TO IMPLEMENT SOURCE: PATTERNS OF ENTERPRISE APPLICATION ARCHITECTURE, MARTIN FOWLER
  10. 10. HOW TO DO DDD THE UBIQUITOUS LANGUAGE
  11. 11. 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
  12. 12. 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(); }
  13. 13. DOMAIN OBJECTS DOMAIN OBJECTS ARE INSTANCES OF REAL ENTITIES THAT HOLD THE BUSINESS LOGIC.
  14. 14. MAIN ELEMENTS OF DDD
  15. 15. ENTITIES ○ 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)
  16. 16. /** @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
  17. 17. 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?
  18. 18. $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());}}
  19. 19. VO BASED ON BOUNDED CONTEXT ○ IN A CUSTOMER MANAGEMENT CONTEXT CUSTOMER SHOULD BE AN ENTITY ○ IN A MEMBERSHIP CONTEXT CUSTOMER SHOULD BE A VALUE OBJECT
  20. 20. SERVICES ○ 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
  21. 21. class OneClickService{ /** * @var ChargecodeAuthcodeValidatorInterface */ protected $_dataAccessor; /** * @var Tc_Bz_HashGenerator_Interface */ protected $_hashGenerator; /** * @var ChargecodeAuthcodeValidationResponseDataFactoryInterface */ protected $_factoryResponceValidate; public function __construct($dataAccessor, $hashGenerator, $factory) { >_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} $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; } }
  22. 22. AGGREGATES ○ 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)
  23. 23. AGGREGATE MEMBERSHIP SITE CUSTOMER TRANSACTION CREDIT CARD EMAIL TRANSACTION CREDIT CARD EMAIL TRANSACTION
  24. 24. FACTORIES ○ PROVIDES ENCAPSULATION FOR OBJECT / AGGREGATE CREATION ○ PRODUCES AN OBJECT IN A CONSISTENT STATE
  25. 25. 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); } }
  26. 26. REPOSITORIES ○ PATTERN FOR RETRIEVING AND SAVING OBJECTS IN THE DB ○ SHOULD NOT BE TIED TO SPECIFIC FRAMEWORK (ORM) ○ EASY SUBSTITUTION FOR TESTING
  27. 27. 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); } }
  28. 28. DEPENDENCY INJECTION ○ 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
  29. 29. “ "Dependency Injection" is a 25-dollar term for a 5-cent concept. [...] Dependency injection means giving an object its instance variables. [...]. - James Shore ”
  30. 30. 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 DIContainer(); $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() ]; PHP-DI
  31. 31. class Tc_Application_Resource_DependencyInjectionContainerResource extends Zend_Application_Resource_ResourceAbstract { public function init() { $this->_container = new DIContainer(); 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 DIDefinitionFileLoaderArrayDefinitionFileLoader($DIResourceFile); } if (0 === substr_compare($DIResourceFile, 'yml', -3, 3, true)) { $file = new DIDefinitionFileLoaderYamlDefinitionFileLoader($DIResourceFile); } /** * Initialize the dependency injection container */ protected function _initDependencyInjection() { $this->bootstrap('DependencyInjectionContainerResource'); $container = $this->getResource('DependencyInjectionContainerResource'); $dispatcher = new DIZendFramework1Dispatcher(); $dispatcher->setContainer($container); $frontController = Zend_Controller_Front::getInstance(); $frontController->setDispatcher($dispatcher); } if (0 === substr_compare($DIResourceFile, 'json', -4, 4, true)) { $file = new DIDefinitionFileLoaderJsonDefinitionFileLoader($DIResourceFile); } if($file === null) { throw new Gamma_Application_Resource_Exception('Invalid Definition File Type'); } return $file; } PHP-DI-ZF1
  32. 32. class Direct_FollowController extends Zend_Controller_Action { /** * @Inject(lazy=true) * @var TcServiceChargeCodeService */ private $_oneClickService; /** * @Inject(lazy=true) * @var TcChargeCodeDataChargecodeAuthcodeGenerationDataFactory */ 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 (TcChargeCodeDataExceptionChargeCodeGenerationDataException $chargeCodeException) { $this->view->requiredParameters = $chargeCodeException; $this->render('charge-code-generation-authcode-invalid-parameters'); } catch (TcChargeCodeDataExceptionChargecodeAuthcodeGenerationResponseDataException $chargeCodeException) { $this->view->requiredParameters = $chargeCodeException; $this->render('charge-code-generation-authcode-invalid-parameters'); } } PHP-DI-ZF1
  33. 33. DOMAIN & SUB-DOMAIN THE HEART OF DDD
  34. 34. 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
  35. 35. SUB-DOMAIN vs BOUNDED CONTEXT ○ 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)
  36. 36. “ ORGANIZATIONS WHICH DESIGN SYSTEMS ARE CONSTRAINED TO PRODUCE DESIGNS WHICH ARE COPIES OF THE COMMUNICATION STRUCTURES OF THESE ORGANIZATIONS - Melvin Conway ”
  37. 37. CONWAY’S LAW SUB-DOMAIN BOUNDARIES ARE DETERMINED IN PART BY THE COMMUNICATION STRUCTURES WITHIN AN ORGANIZATION
  38. 38. BOUNDED CONTEXTS ○ CODE BASE FOR DOMAIN MODEL CONTEXT ○ EVERY MODEL’S PROPERTIES AND OPERATIONS HAS SPECIAL MEANING WITHIN THE SPECIFIC CONTEXT
  39. 39. ENTITY VALUE OBJECT
  40. 40. 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
  41. 41. 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
  42. 42. “ Any 3rd party system that I have to integrate with, was written by a drunken monkey typing with his feet - Oren Eini ”
  43. 43. SUB-DOMAIN ANTICORRUPTION LAYER THAT TRANSLATES USER/ROLES BETWEEN SUBDOMAINS SUB-DOMAIN
  44. 44. “ Any fool can write code that a computer can understand. Good programmers write code that humans can understand. - Martin Fowler ”
  45. 45. REFERENCES DOMAIN-DRIVEN DESIGN BY ERIC EVANS IMPLEMENTING DOMAIN-DRIVEN DESIGN BY VAUGHN VERNON

×