SlideShare a Scribd company logo
1 of 31
Download to read offline
Pursuing
 practices of
Domain-Driven
   Design
   in PHP
Who am I

   Giorgio Sironi
Bachelor in Computer
     Engineering
  Advisor @ Allbus
Zone Leader @ DZone
The long title



   Pursuing
  practices of
 Domain-Driven
    Design
    in PHP
The DDD box of goods
Knowledge crunching
Ubiquitous Language
Domain Model
Refactoring towards deeper
insight...
Collaboration patterns (Anti-
corruption layer, Separate
ways, ...)
Why? (the most important slide)
 Close to business, to follow its changes
   aids iterative development
 The code is the design
   supported by blackboards and UML
 Test and develop with in-memory objects
   no instantiation of Oracle connections
 It's also fun!
   exercise creativity and learning skills
Domain Model
Reflects knowledge of the domain more than
technology
  While there is value in the item on the right, we
  value the item on the left more
  Persistence-agnostic (UnitOfWork)
  In general, no outward dependencies
Step 1: from relational...
user
                               group
id
                               id
username
                               name
password
                               logo
active

                  user_group
                  id_user
                  id_group
Step 1: ...to object-oriented




class User          class Group
{                   {
}                   }
Step 2: from Active Record...
 class Group extends Doctrine_Record
 {
 }


 Hard dependency towards ORM
 Inherits pollution from Doctrine_Record
Step 2: … to Data Mapper
/**
    * @Entity
    */
class User
{
}
Building blocks
Entity
Value Object
Aggregate
Repository
Factory
Service
Other transient objects (Specification,
Parameter Objects, ...)
Entity
More than a row
Equality is based on identity
  e.g. Post #42, user 'giorgiosironi', …
The bread and butter of your Domain Model
Value Object
Previously known in the world as value
  e.g. the number 42, Zip code 22031, #FF0000
Equality based on values
Immutable in certain implementations
Aggregate
Subgraph of Entities and Value Objects
  with a single entry: the root
  possible multiple exits, mostly to follow during
  reading
Unit of consistency: loose correspondency to
database transactions
Partitions the state of the application
Checkpoint: data modeling
Repository
The gate to the database
  One aggregate at the time
The illusion of an in-memory collection of
Entities
  Fowler's definition
  Here's a BookRepository
Factory
Encapsulate creation of complex Aggregates
  a new() is often all you need
Service
At the domain level
To help Entities and Value Objects
  to avoid mutual dependencies
  or field references to strangers
    optimal for isolation from libraries
Checkpoint: lifecycle classes
Meanwhile, in PHP...
On frameworks




Only one suggestion: build your Domain Model
       like if the framework didn't exist
Active Record vs. Data Mapper


Doctrine 2 for persistence (see tutorial by
@juokaz)

By default for Doctrine 2 object === row

All the tricks are at
http://github.com/giorgiosironi/ddd-talk
Entity
@Entity annotation
  no extends
@Column for fields
  private fields
  types are PHP types: string, not varchar
Value Objects
Do you want an (id, date) table?
  Serialization of the whole object
  Conversion into a custom string/numerical
  format via a custom DBAL data type
  Deconstruction/reconstruction with lifecycle
  hooks
  Combined approaches: serialization and mirror
  fields
Aggregate
    Relationships: @OneToMany, @ManyToMany,
    @OneToOne, …
●   @OneToMany(..., cascade={“persist”,
    “remove”})
●   @OneToMany(..., orphanRemoval=true)
●   @ManyToMany(targetEntity="Phonenumber")
    @JoinTable(...,
    inverseJoinColumns={name=”phonenumber_id",
    referencedColumnName="id", unique=true)
Repository
Plain Old PHP Object
Composing EntityManager
  It's possible to define EntityManager custom
  repositories: quick and dirty
Encapsulates queries
Typical methods: add($root), remove($root),
find($id), findByStrangeCriteria()
  It's a collection!
Factory, Service
POPO
  Sometimes composing services or infrastructure
  objects (e.g. generating new progressive number
  for invoices, calculate current taxes, sending
  mails...)
Often decoupled with an interface
References
The code shown in this talk
  http://github.com/giorgiosironi/ddd-talk
Four books
  http://domaindrivendesign.org/books
Domain-Driven Design mailing list
  Google that :)
Q/A
Feedback




Testing in isolation tutorial: http://joind.in/3216

         DDD talk: http://joind.in/3224
Thanks for your attention

More Related Content

What's hot

Carlos Amador .Net Portfolio
Carlos Amador .Net PortfolioCarlos Amador .Net Portfolio
Carlos Amador .Net Portfolio
CMA_SlideShare
 
jpa-hibernate-presentation
jpa-hibernate-presentationjpa-hibernate-presentation
jpa-hibernate-presentation
John Slick
 
Understanding Reflection
Understanding ReflectionUnderstanding Reflection
Understanding Reflection
Tamir Khason
 

What's hot (20)

Carlos Amador .Net Portfolio
Carlos Amador .Net PortfolioCarlos Amador .Net Portfolio
Carlos Amador .Net Portfolio
 
CORBA
CORBACORBA
CORBA
 
Dom structures
Dom structuresDom structures
Dom structures
 
Introduction to Hibernate Framework
Introduction to Hibernate FrameworkIntroduction to Hibernate Framework
Introduction to Hibernate Framework
 
2. overview of c#
2. overview of c#2. overview of c#
2. overview of c#
 
Sterling for Windows Phone 7
Sterling for Windows Phone 7Sterling for Windows Phone 7
Sterling for Windows Phone 7
 
pebble - Building apps on pebble
pebble - Building apps on pebblepebble - Building apps on pebble
pebble - Building apps on pebble
 
Introduction to JPA and Hibernate including examples
Introduction to JPA and Hibernate including examplesIntroduction to JPA and Hibernate including examples
Introduction to JPA and Hibernate including examples
 
Extending the Xbase Typesystem
Extending the Xbase TypesystemExtending the Xbase Typesystem
Extending the Xbase Typesystem
 
Solid OOPS
Solid OOPSSolid OOPS
Solid OOPS
 
Hibernate
HibernateHibernate
Hibernate
 
Hibernate
HibernateHibernate
Hibernate
 
Choose'10: Ralf Laemmel - Dealing Confortably with the Confusion of Tongues
Choose'10: Ralf Laemmel - Dealing Confortably with the Confusion of TonguesChoose'10: Ralf Laemmel - Dealing Confortably with the Confusion of Tongues
Choose'10: Ralf Laemmel - Dealing Confortably with the Confusion of Tongues
 
jpa-hibernate-presentation
jpa-hibernate-presentationjpa-hibernate-presentation
jpa-hibernate-presentation
 
Understanding Reflection
Understanding ReflectionUnderstanding Reflection
Understanding Reflection
 
Design patterns - Proxy & Composite
Design patterns - Proxy & CompositeDesign patterns - Proxy & Composite
Design patterns - Proxy & Composite
 
Hibernate in Nutshell
Hibernate in NutshellHibernate in Nutshell
Hibernate in Nutshell
 
Hibernate tutorial
Hibernate tutorialHibernate tutorial
Hibernate tutorial
 
Lecture4 corba
Lecture4   corbaLecture4   corba
Lecture4 corba
 
JPA For Beginner's
JPA For Beginner'sJPA For Beginner's
JPA For Beginner's
 

Viewers also liked

Viewers also liked (6)

An Introduction to Domain Driven Design in PHP
An Introduction to Domain Driven Design in PHPAn Introduction to Domain Driven Design in PHP
An Introduction to Domain Driven Design in PHP
 
An year of Pomodoros
An year of PomodorosAn year of Pomodoros
An year of Pomodoros
 
#pugMi - DDD - Value objects
#pugMi - DDD - Value objects#pugMi - DDD - Value objects
#pugMi - DDD - Value objects
 
Implementing DDD Concepts in PHP
Implementing DDD Concepts in PHPImplementing DDD Concepts in PHP
Implementing DDD Concepts in PHP
 
Clean architecture with ddd layering in php
Clean architecture with ddd layering in phpClean architecture with ddd layering in php
Clean architecture with ddd layering in php
 
Software Design Patterns in Laravel by Phill Sparks
Software Design Patterns in Laravel by Phill SparksSoftware Design Patterns in Laravel by Phill Sparks
Software Design Patterns in Laravel by Phill Sparks
 

Similar to Pursuing practices of Domain-Driven Design in PHP

Introduction To Dojo
Introduction To DojoIntroduction To Dojo
Introduction To Dojo
yoavrubin
 
I assignmnt(oops)
I assignmnt(oops)I assignmnt(oops)
I assignmnt(oops)
Jay Patel
 

Similar to Pursuing practices of Domain-Driven Design in PHP (20)

From Java to Python: beating the Stockholm syndrome
From Java to Python: beating the Stockholm syndromeFrom Java to Python: beating the Stockholm syndrome
From Java to Python: beating the Stockholm syndrome
 
Lotusphere 2007 BP301 Advanced Object Oriented Programming for LotusScript
Lotusphere 2007 BP301 Advanced Object Oriented Programming for LotusScriptLotusphere 2007 BP301 Advanced Object Oriented Programming for LotusScript
Lotusphere 2007 BP301 Advanced Object Oriented Programming for LotusScript
 
Interview preparation for programming.pptx
Interview preparation for programming.pptxInterview preparation for programming.pptx
Interview preparation for programming.pptx
 
Php oop (1)
Php oop (1)Php oop (1)
Php oop (1)
 
Unit 5.ppt
Unit 5.pptUnit 5.ppt
Unit 5.ppt
 
Bp301
Bp301Bp301
Bp301
 
Minds-on DDD
Minds-on DDDMinds-on DDD
Minds-on DDD
 
Lotusphere 2007 AD507 Leveraging the Power of Object Oriented Programming in ...
Lotusphere 2007 AD507 Leveraging the Power of Object Oriented Programming in ...Lotusphere 2007 AD507 Leveraging the Power of Object Oriented Programming in ...
Lotusphere 2007 AD507 Leveraging the Power of Object Oriented Programming in ...
 
Introduction To Dojo
Introduction To DojoIntroduction To Dojo
Introduction To Dojo
 
Python Deserialization Attacks
Python Deserialization AttacksPython Deserialization Attacks
Python Deserialization Attacks
 
Libertyvasion2010
Libertyvasion2010Libertyvasion2010
Libertyvasion2010
 
Java basics
Java basicsJava basics
Java basics
 
I assignmnt(oops)
I assignmnt(oops)I assignmnt(oops)
I assignmnt(oops)
 
Entity Framework Today (May 2012)
Entity Framework Today (May 2012)Entity Framework Today (May 2012)
Entity Framework Today (May 2012)
 
Green dao
Green daoGreen dao
Green dao
 
CPP_,module2_1.pptx
CPP_,module2_1.pptxCPP_,module2_1.pptx
CPP_,module2_1.pptx
 
NHibernate
NHibernateNHibernate
NHibernate
 
Java Basics
Java BasicsJava Basics
Java Basics
 
LINQ 2 SQL Presentation To Palmchip And Trg, Technology Resource Group
LINQ 2 SQL Presentation To Palmchip  And Trg, Technology Resource GroupLINQ 2 SQL Presentation To Palmchip  And Trg, Technology Resource Group
LINQ 2 SQL Presentation To Palmchip And Trg, Technology Resource Group
 
chapter 5 Objectdesign.ppt
chapter 5 Objectdesign.pptchapter 5 Objectdesign.ppt
chapter 5 Objectdesign.ppt
 

More from Giorgio Sironi

Case study: Khan Academy
Case study: Khan AcademyCase study: Khan Academy
Case study: Khan Academy
Giorgio Sironi
 
Case study: iTunes for K-12
Case study: iTunes for K-12Case study: iTunes for K-12
Case study: iTunes for K-12
Giorgio Sironi
 
Case study: Innovascuola
Case study: InnovascuolaCase study: Innovascuola
Case study: Innovascuola
Giorgio Sironi
 
Case study: e-Learning for Kids
Case study: e-Learning for KidsCase study: e-Learning for Kids
Case study: e-Learning for Kids
Giorgio Sironi
 
Case study: Chocolat 3B
Case study: Chocolat 3BCase study: Chocolat 3B
Case study: Chocolat 3B
Giorgio Sironi
 

More from Giorgio Sironi (19)

Case study: Khan Academy
Case study: Khan AcademyCase study: Khan Academy
Case study: Khan Academy
 
Case study: iTunes for K-12
Case study: iTunes for K-12Case study: iTunes for K-12
Case study: iTunes for K-12
 
Case study: Insegnalo
Case study: InsegnaloCase study: Insegnalo
Case study: Insegnalo
 
Case study: Innovascuola
Case study: InnovascuolaCase study: Innovascuola
Case study: Innovascuola
 
Case study: e-Learning for Kids
Case study: e-Learning for KidsCase study: e-Learning for Kids
Case study: e-Learning for Kids
 
Case study: Chocolat 3B
Case study: Chocolat 3BCase study: Chocolat 3B
Case study: Chocolat 3B
 
Khan Academy
Khan AcademyKhan Academy
Khan Academy
 
Itunes K-12
Itunes K-12Itunes K-12
Itunes K-12
 
Insegnalo
InsegnaloInsegnalo
Insegnalo
 
Innovascuola
InnovascuolaInnovascuola
Innovascuola
 
e-Learning for kids
e-Learning for kidse-Learning for kids
e-Learning for kids
 
Chocolat 3B
Chocolat 3BChocolat 3B
Chocolat 3B
 
Blind detection of image manipulation @ PoliMi
Blind detection of image manipulation @ PoliMiBlind detection of image manipulation @ PoliMi
Blind detection of image manipulation @ PoliMi
 
CouchDB @ PoliMi
CouchDB @ PoliMiCouchDB @ PoliMi
CouchDB @ PoliMi
 
Cohesion and coupling metrics for workflow process design
Cohesion and coupling metrics for workflow process designCohesion and coupling metrics for workflow process design
Cohesion and coupling metrics for workflow process design
 
PHP Barcelona 2010 - Architecture and testability
PHP Barcelona 2010 - Architecture and testabilityPHP Barcelona 2010 - Architecture and testability
PHP Barcelona 2010 - Architecture and testability
 
Chansonnier: web application for multimedia search on song videos
Chansonnier: web application for multimedia search on song videosChansonnier: web application for multimedia search on song videos
Chansonnier: web application for multimedia search on song videos
 
Chansonnier: web application for multimedia search on song videos
Chansonnier: web application for multimedia search on song videosChansonnier: web application for multimedia search on song videos
Chansonnier: web application for multimedia search on song videos
 
Php day2010
Php day2010Php day2010
Php day2010
 

Recently uploaded

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Recently uploaded (20)

MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 

Pursuing practices of Domain-Driven Design in PHP

  • 2. Who am I Giorgio Sironi Bachelor in Computer Engineering Advisor @ Allbus Zone Leader @ DZone
  • 3. The long title Pursuing practices of Domain-Driven Design in PHP
  • 4. The DDD box of goods Knowledge crunching Ubiquitous Language Domain Model Refactoring towards deeper insight... Collaboration patterns (Anti- corruption layer, Separate ways, ...)
  • 5. Why? (the most important slide) Close to business, to follow its changes aids iterative development The code is the design supported by blackboards and UML Test and develop with in-memory objects no instantiation of Oracle connections It's also fun! exercise creativity and learning skills
  • 6. Domain Model Reflects knowledge of the domain more than technology While there is value in the item on the right, we value the item on the left more Persistence-agnostic (UnitOfWork) In general, no outward dependencies
  • 7. Step 1: from relational... user group id id username name password logo active user_group id_user id_group
  • 8. Step 1: ...to object-oriented class User class Group { { } }
  • 9. Step 2: from Active Record... class Group extends Doctrine_Record { } Hard dependency towards ORM Inherits pollution from Doctrine_Record
  • 10. Step 2: … to Data Mapper /** * @Entity */ class User { }
  • 11. Building blocks Entity Value Object Aggregate Repository Factory Service Other transient objects (Specification, Parameter Objects, ...)
  • 12. Entity More than a row Equality is based on identity e.g. Post #42, user 'giorgiosironi', … The bread and butter of your Domain Model
  • 13. Value Object Previously known in the world as value e.g. the number 42, Zip code 22031, #FF0000 Equality based on values Immutable in certain implementations
  • 14. Aggregate Subgraph of Entities and Value Objects with a single entry: the root possible multiple exits, mostly to follow during reading Unit of consistency: loose correspondency to database transactions Partitions the state of the application
  • 16. Repository The gate to the database One aggregate at the time The illusion of an in-memory collection of Entities Fowler's definition Here's a BookRepository
  • 17. Factory Encapsulate creation of complex Aggregates a new() is often all you need
  • 18. Service At the domain level To help Entities and Value Objects to avoid mutual dependencies or field references to strangers optimal for isolation from libraries
  • 21. On frameworks Only one suggestion: build your Domain Model like if the framework didn't exist
  • 22. Active Record vs. Data Mapper Doctrine 2 for persistence (see tutorial by @juokaz) By default for Doctrine 2 object === row All the tricks are at http://github.com/giorgiosironi/ddd-talk
  • 23. Entity @Entity annotation no extends @Column for fields private fields types are PHP types: string, not varchar
  • 24. Value Objects Do you want an (id, date) table? Serialization of the whole object Conversion into a custom string/numerical format via a custom DBAL data type Deconstruction/reconstruction with lifecycle hooks Combined approaches: serialization and mirror fields
  • 25. Aggregate Relationships: @OneToMany, @ManyToMany, @OneToOne, … ● @OneToMany(..., cascade={“persist”, “remove”}) ● @OneToMany(..., orphanRemoval=true) ● @ManyToMany(targetEntity="Phonenumber") @JoinTable(..., inverseJoinColumns={name=”phonenumber_id", referencedColumnName="id", unique=true)
  • 26. Repository Plain Old PHP Object Composing EntityManager It's possible to define EntityManager custom repositories: quick and dirty Encapsulates queries Typical methods: add($root), remove($root), find($id), findByStrangeCriteria() It's a collection!
  • 27. Factory, Service POPO Sometimes composing services or infrastructure objects (e.g. generating new progressive number for invoices, calculate current taxes, sending mails...) Often decoupled with an interface
  • 28. References The code shown in this talk http://github.com/giorgiosironi/ddd-talk Four books http://domaindrivendesign.org/books Domain-Driven Design mailing list Google that :)
  • 29. Q/A
  • 30. Feedback Testing in isolation tutorial: http://joind.in/3216 DDD talk: http://joind.in/3224
  • 31. Thanks for your attention