An Introduction to Domain Driven Design in PHP Chris Renner Nashville PHP Meetup August 9, 2016
Agenda • A bit about me & what I do • Describe our principle application and its purpose • High-level DDD concepts • Implementation strategies and patterns • Lessons learned • Conclusion
About Me - Personal • Married 17 years with two little ones (10 and 3) • In Nashville 18 years, originally from Kentucky • Interests: Too many!
The Renners
About Me - Professional • Sr. Application Developer at VUMC, 12 years at Vanderbilt,~10 years as PHP dev • Self-taught developer* • internal-facing enterprise scale apps • Prior: proposal writer, contract negotiator
What I do • Application Development • User interaction & support • Business Analysis • Reporting and analytics • Support one enterprise app, a few small apps and a couple of websites
Our Main App • PEER • Went online July 2007 • Primary business application of a department of 30 users, with hundreds of other “customers”. • Domain is “Contract Management”
What is DDD? • A set of solutions to documenting, solving and managing the complexity of business problems in your code. • A methodology for thinking and talking about solving business problems with code
DDD is not… • a TV show on Food Network • anything to do with domain names or DNS • code, but rather principles and practices. • MVC or an MVC replacement. • a design pattern. • an all-or-nothing proposition.
DDD is… • a process • modeling business logic in code • a common language among devs and stakeholders • agile*
When to use? • Complicated business logic • Large code base • Multiple devs • Long dev cycle, app lifecycle • moving paper/human processes into an electronic system
When NOT to use? • Short development cycle • Microservices • Simple business models • Minimal business logic
Requirements • General intelligence - must understand client’s business process • Communication skills - must be able to translate biz speak to tech speak and vice versa • Humility
The Big Picture
Domain • A sphere of knowledge, influence or activity. The subject area to which the user applies a program is the domain of the software • e.g. Used Car Sales, Patient Medical Records, Contract Management* • not Automotive, Health Care
Model • A software abstraction representing a specific concept in the domain. • Data & Biz Logic • Will be continually refined as understanding of domain grows.
Ubiquitous Language • Ubiquitous: omnipresent, everywhere • Set of terms structured around the domain model and used by both tech and biz team members when discussing the project. • Use in the code
Bounded Context • Limits drawn around the scope of the business problem create a context area. • Statements about a model can only be understood within this context.
“Tactical” DDD
Entities • e.g. Car, Patient, Invoice, Contract • Typically 1-class per entity • Mimic real objects • Data + Business Logic = Domain Models
class Widget { public $id; public $price; public function __construct() { } public function isDiscountable() { // logic here } }
Factories • Objects that create instances of other objects • TIP: Pass in an ID to get back hydrated instance
class Widget { public static function factory($id = null) { // create instance $obj = new Widget(); // hydrate with data from persistence if ($id) { WidgetMapper::hydrate($obj, $id); } return $obj; } } $foo = Widget::factory(1234);
Aggregates • One-to-Many • E.g. Entity “Invoice” may have multiple line items, each probably a LineItem Entity • Roll up many like things into a parent
Domain Services • Use sparingly! • Capture business logic that operates on more than one entity/model • When a class isn’t a “thing” • != application services
Domain Events • Real actions, events or activities in the business • Becomes your domain logic • Place in model/entity or service layer
Modules • Separate code into meaningful subject areas based on business logic • One module for system/application support/architectural stuff, then modules for each business sub-area or bundle of related code • Personal preference
Layers (Hexagonal Architecture) • Separation of concerns • Outer later is interface, either UI or service connections • middle layer that translates requests into business actions • inner layer is business logic
Patterns and Best Practices
Domain Models • Implementation of Entity • Use Inheritance and/or Traits & Interfaces to abstract and re-use • Domain Model + Mapper + Table Data Gateway patterns
Domain Model Mapper Table • All SQL in Table class • Mapper translates between Model properties and table column • Model has NO awareness of the Table class • Application code (controllers, services) interact with the Model only
class Widget extends DomainModelAbstract { public $id; public $name; public $price; protected $department; public static function factory($id = null) { // create instance $obj = new Widget(); // hydrate with data from persistence if ($id) { WidgetMapper::hydrate($obj, $id); } return $obj; } }
class WidgetTable extends TableAbstract { public $name = ‘WIDGET_TABLE’; public $primary = ‘ID’; public function fetchById($id) { $sql = “SELECT * FROM $this->name WHERE $this->primary = :id”; $result = $db->query($sql, [‘:id’ = > $id]); return $result->fetch(); } }
class WidgetMapper extends MapperAbstract { public static $columns = [ ‘id’ => ‘ID’, ‘name’ => ‘NAME, ‘price’ => ‘PRICE’ ] }
Interacting with a Domain Model Instance $myWidget = Widget::factory(); $myWidget->setName(‘Acme Super Thingy’); $myWidget->price = 99.95; $id = $myWidget->save(); echo $id; // 1234
Lazy Loading class Widget { public $deptId; private $department; public function getDept() { if (!$this->department) { $this->department = Department::factory($this->deptId); } return $this->department; } } echo $widget->getDept()->name; // Human Resources
Getters and Setters • Don’t Overuse! • Opportunity to Add Logic
public function setName($string) { $this->name = substr($string, 5, 100); } public function setStatus($status) { if($status != $this->status) { $this->addtoStatusLog($status); } $this->status = $status; }
Strategy Pattern • aka “Policy Pattern” • encapsulate business rules/logic
Strategy Pattern Example class Contract { public function isExpired() { if(strtotime($this->endDate) < time()) { return true; } return false; } } $contract->setEndDate(‘2016-08-01’); echo $contract->isExpired(); // true
Specification Pattern • like Strategy, but a separate class instead of a function • single public function isSatisfiedBy() determines if an object meets specified criteria • can be chained
Specification Pattern Example class BillingAgreementSpecification { public function isSatisfiedBy(Contract $contract) { if(!$contract->requirementA) { return false; } if(!$contract->requirementB) { return false; } return true; } } $spec = new BillingAgreementSpecification(); echo $spec->isSatisfiedBy($contract); // true
Lessons Learned
Intention-Revealing Interface • Contextually Relevant Variable, Function and Class Names • public function sendFullyExecutedEmail()
Comment Your Code • comment and docblock everything
IDs for Everything! • usernames and emails are NOT unique IDs! • give everything an internal numeric ID, even if its never seen by user
Minimize Shortcuts • Code it to work first, then go back and make it right • hack-y code will bite you later • iterate + feedback, don’t assume you fully understand the problem - the Model will never be “done”
Avoid Framework Lock-In • Preference for components • Composer • Consider extending a Micro-Framework v. an all-in- one package
Good OOP required • Abstraction • Encapsulation • Separation of Concerns
Avoid Unnecessary Complexity • Ask “is this necessary?” • Don’t let DDD rules become a prison • Simplicity = Maintainability
Become the Expert • You will uncover flaws and inefficiencies in the business logic • You may end up understanding the business process better than stakeholders
Summary • DDD a way of communicating and thinking about a complex business problem • Implementing DDD involves the best of enterprise design patterns, OOP and clean code principles. • Use what works, every project is different
References • Domain Driven Design: Tackling Complexity in the Heart of Software - Eric Evans, 2003 • DDD Reference - Eric Evans, https://www.domainlanguage.com/ddd/reference/ • Clean Code: A Handbook of Agile Software Craftsmanship - Robert Martin, 2008 • Patterns of Enterprise Application Architecture - Martin Fowler, 2002 • phproundtable: Domain Driven Design in PHP - https://www.phproundtable.com/episode/domain-driven-design- in-php
Find Me • Twitter: @rennerchris • Web: www.chrisrenner.com • Email: rennercr@gmail.com
I’m Troy McClure, and you might remember me from such tech talks as “Creating Legacy Code” and “Maximizing Technical Debt”
Questions?

An Introduction to Domain Driven Design in PHP

  • 1.
    An Introduction toDomain Driven Design in PHP Chris Renner Nashville PHP Meetup August 9, 2016
  • 2.
    Agenda • A bitabout me & what I do • Describe our principle application and its purpose • High-level DDD concepts • Implementation strategies and patterns • Lessons learned • Conclusion
  • 3.
    About Me -Personal • Married 17 years with two little ones (10 and 3) • In Nashville 18 years, originally from Kentucky • Interests: Too many!
  • 4.
  • 5.
    About Me -Professional • Sr. Application Developer at VUMC, 12 years at Vanderbilt,~10 years as PHP dev • Self-taught developer* • internal-facing enterprise scale apps • Prior: proposal writer, contract negotiator
  • 6.
    What I do •Application Development • User interaction & support • Business Analysis • Reporting and analytics • Support one enterprise app, a few small apps and a couple of websites
  • 7.
    Our Main App •PEER • Went online July 2007 • Primary business application of a department of 30 users, with hundreds of other “customers”. • Domain is “Contract Management”
  • 8.
    What is DDD? •A set of solutions to documenting, solving and managing the complexity of business problems in your code. • A methodology for thinking and talking about solving business problems with code
  • 9.
    DDD is not… •a TV show on Food Network • anything to do with domain names or DNS • code, but rather principles and practices. • MVC or an MVC replacement. • a design pattern. • an all-or-nothing proposition.
  • 10.
    DDD is… • aprocess • modeling business logic in code • a common language among devs and stakeholders • agile*
  • 11.
    When to use? •Complicated business logic • Large code base • Multiple devs • Long dev cycle, app lifecycle • moving paper/human processes into an electronic system
  • 12.
    When NOT touse? • Short development cycle • Microservices • Simple business models • Minimal business logic
  • 13.
    Requirements • General intelligence- must understand client’s business process • Communication skills - must be able to translate biz speak to tech speak and vice versa • Humility
  • 14.
  • 15.
    Domain • A sphereof knowledge, influence or activity. The subject area to which the user applies a program is the domain of the software • e.g. Used Car Sales, Patient Medical Records, Contract Management* • not Automotive, Health Care
  • 16.
    Model • A softwareabstraction representing a specific concept in the domain. • Data & Biz Logic • Will be continually refined as understanding of domain grows.
  • 17.
    Ubiquitous Language • Ubiquitous:omnipresent, everywhere • Set of terms structured around the domain model and used by both tech and biz team members when discussing the project. • Use in the code
  • 18.
    Bounded Context • Limitsdrawn around the scope of the business problem create a context area. • Statements about a model can only be understood within this context.
  • 19.
  • 20.
    Entities • e.g. Car,Patient, Invoice, Contract • Typically 1-class per entity • Mimic real objects • Data + Business Logic = Domain Models
  • 21.
    class Widget { public$id; public $price; public function __construct() { } public function isDiscountable() { // logic here } }
  • 22.
    Factories • Objects thatcreate instances of other objects • TIP: Pass in an ID to get back hydrated instance
  • 23.
    class Widget { public staticfunction factory($id = null) { // create instance $obj = new Widget(); // hydrate with data from persistence if ($id) { WidgetMapper::hydrate($obj, $id); } return $obj; } } $foo = Widget::factory(1234);
  • 24.
    Aggregates • One-to-Many • E.g.Entity “Invoice” may have multiple line items, each probably a LineItem Entity • Roll up many like things into a parent
  • 25.
    Domain Services • Usesparingly! • Capture business logic that operates on more than one entity/model • When a class isn’t a “thing” • != application services
  • 26.
    Domain Events • Realactions, events or activities in the business • Becomes your domain logic • Place in model/entity or service layer
  • 27.
    Modules • Separate codeinto meaningful subject areas based on business logic • One module for system/application support/architectural stuff, then modules for each business sub-area or bundle of related code • Personal preference
  • 28.
    Layers (Hexagonal Architecture) • Separationof concerns • Outer later is interface, either UI or service connections • middle layer that translates requests into business actions • inner layer is business logic
  • 29.
  • 30.
    Domain Models • Implementationof Entity • Use Inheritance and/or Traits & Interfaces to abstract and re-use • Domain Model + Mapper + Table Data Gateway patterns
  • 31.
    Domain Model Mapper Table • AllSQL in Table class • Mapper translates between Model properties and table column • Model has NO awareness of the Table class • Application code (controllers, services) interact with the Model only
  • 32.
    class Widget extendsDomainModelAbstract { public $id; public $name; public $price; protected $department; public static function factory($id = null) { // create instance $obj = new Widget(); // hydrate with data from persistence if ($id) { WidgetMapper::hydrate($obj, $id); } return $obj; } }
  • 33.
    class WidgetTable extendsTableAbstract { public $name = ‘WIDGET_TABLE’; public $primary = ‘ID’; public function fetchById($id) { $sql = “SELECT * FROM $this->name WHERE $this->primary = :id”; $result = $db->query($sql, [‘:id’ = > $id]); return $result->fetch(); } }
  • 34.
    class WidgetMapper extendsMapperAbstract { public static $columns = [ ‘id’ => ‘ID’, ‘name’ => ‘NAME, ‘price’ => ‘PRICE’ ] }
  • 35.
    Interacting with aDomain Model Instance $myWidget = Widget::factory(); $myWidget->setName(‘Acme Super Thingy’); $myWidget->price = 99.95; $id = $myWidget->save(); echo $id; // 1234
  • 36.
    Lazy Loading class Widget { public$deptId; private $department; public function getDept() { if (!$this->department) { $this->department = Department::factory($this->deptId); } return $this->department; } } echo $widget->getDept()->name; // Human Resources
  • 37.
    Getters and Setters •Don’t Overuse! • Opportunity to Add Logic
  • 38.
    public function setName($string) { $this->name= substr($string, 5, 100); } public function setStatus($status) { if($status != $this->status) { $this->addtoStatusLog($status); } $this->status = $status; }
  • 39.
    Strategy Pattern • aka“Policy Pattern” • encapsulate business rules/logic
  • 40.
    Strategy Pattern Example classContract { public function isExpired() { if(strtotime($this->endDate) < time()) { return true; } return false; } } $contract->setEndDate(‘2016-08-01’); echo $contract->isExpired(); // true
  • 41.
    Specification Pattern • likeStrategy, but a separate class instead of a function • single public function isSatisfiedBy() determines if an object meets specified criteria • can be chained
  • 42.
    Specification Pattern Example classBillingAgreementSpecification { public function isSatisfiedBy(Contract $contract) { if(!$contract->requirementA) { return false; } if(!$contract->requirementB) { return false; } return true; } } $spec = new BillingAgreementSpecification(); echo $spec->isSatisfiedBy($contract); // true
  • 43.
  • 44.
    Intention-Revealing Interface • ContextuallyRelevant Variable, Function and Class Names • public function sendFullyExecutedEmail()
  • 45.
    Comment Your Code •comment and docblock everything
  • 46.
    IDs for Everything! •usernames and emails are NOT unique IDs! • give everything an internal numeric ID, even if its never seen by user
  • 47.
    Minimize Shortcuts • Codeit to work first, then go back and make it right • hack-y code will bite you later • iterate + feedback, don’t assume you fully understand the problem - the Model will never be “done”
  • 48.
    Avoid Framework Lock-In •Preference for components • Composer • Consider extending a Micro-Framework v. an all-in- one package
  • 49.
    Good OOP required •Abstraction • Encapsulation • Separation of Concerns
  • 50.
    Avoid Unnecessary Complexity • Ask“is this necessary?” • Don’t let DDD rules become a prison • Simplicity = Maintainability
  • 51.
    Become the Expert •You will uncover flaws and inefficiencies in the business logic • You may end up understanding the business process better than stakeholders
  • 52.
    Summary • DDD away of communicating and thinking about a complex business problem • Implementing DDD involves the best of enterprise design patterns, OOP and clean code principles. • Use what works, every project is different
  • 53.
    References • Domain DrivenDesign: Tackling Complexity in the Heart of Software - Eric Evans, 2003 • DDD Reference - Eric Evans, https://www.domainlanguage.com/ddd/reference/ • Clean Code: A Handbook of Agile Software Craftsmanship - Robert Martin, 2008 • Patterns of Enterprise Application Architecture - Martin Fowler, 2002 • phproundtable: Domain Driven Design in PHP - https://www.phproundtable.com/episode/domain-driven-design- in-php
  • 54.
    Find Me • Twitter:@rennerchris • Web: www.chrisrenner.com • Email: rennercr@gmail.com
  • 55.
    I’m Troy McClure,and you might remember me from such tech talks as “Creating Legacy Code” and “Maximizing Technical Debt”
  • 56.