4. and
All these tools were/are/will
be the “best choice”
Your domain should not worry
about that
5. Domain-driven design
BasketWasCreated or BasketWasPickedUp
Concept created by Eric Evans (2003)
It is an approach of software development, designed to work with complex
and large scaled software
It proposes an Ubiquitous language between business and software
(developers and business experts)
It focuses on domain and business
6. When should we use it?
When should we not use it?
Complex domain with many business rules
Iterative process with a long lifecycle
Growth forecast of complexity
Simple domains (like CRUDs)
7. Why do we use it?
Agile, iterative and continuous modelling
Less coupling, more cohesive, flexible and extensible code
Testable Code
Agreement between domain experts and developers
BDD tests close to the code
Software for everyone to understand
10. Entities
Have an identity that endures over the time
1 class User {
2 private $username;
3 private $password;
4
5 public function getUsername() : string {
6 return $this->username;
7 }
8
9 public function setUsername(string $username) {
10 $this->username = $username;
11 }
12
13 public function getPassword() : string {
14 return $this->password;
15 }
16
17 public function setPassword(string $password) {
18 $this->password = $password;
19 }
20 }
Don’t forget the PSR-1 :P
11. Entities
Entities have behaviourEntities aren't typed arrays (getters e setters)
1 class User {
2 private $banned;
3 private $username;
4 private $passwordHash;
5
6 public function toNickname() : string {
7 return $this->username;
8 }
9
10 public function authenticate(string $pass, callable $checkHash) : bool {
11 return $checkHash($pass, $this->passwordHash)
12 && ! $this->hasActiveBans();
13 }
14
15 public function changePass(string $pass, callable $hash) {
16 $this->passwordHash = $hash($pass);
17 }
18 }
beberlei/assert - Thin assertion library for use in libraries and business-model
12. Entities
Keep collections hidden in your entities
Disallow Collection access from outside the entity
1 public function banUser(Uuid $userId)
2 {
3 $user = $this->repository->find($userId);
4
5 $user->getBans()->add(new Ban($user));
6 }
1 class User
2 {
3 private $bans;
4
5 public function getBans() : Collection {
6 return $this->bans;
7 }
8 }
1 class User
2 {
3 private $roles;
4
5 public function promote(Role $role) {
6 $this->roles[] = $role;
7 }
8
9 public function is(Role $role) : bool {
10 return $this->roles->has($role);
11 }
12 }
13. Entities
1 class User {
2 private $id;
3 private $username;
4
5 public function __construct(string $username) {
6 $this->id = Uuid::uuid4();
7 $this->username = $username;
8 }
9 }
Your db operations will block each other
You are denying bulk inserts
You cannot make multi-request transactions
Your object is invalid until saved
Your object does not work without the DB
Immutable data is simple
Immutable data is cacheable (forever)
Immutable data is predictable
Immutable data enables historical analysis
Or append-only data-structures
Avoid Auto-generated Identifiers Stay valid after __construct
Avoid Soft-deletes
Avoid setters
DateTimeImmutable
15. Value Objects
Martin Fowler defines a Value Object as a small object such as money or a date range object.
Examples: numbers, text strings, dates, times, a person’s full name (composed
of first, middle, last name, and title), currencies, colours, phone numbers, and postal code.
1 class Email {
2 public function __construct($email) {
3 if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
4 throw new InvalidArgumentException('...');
5 }
6
7 $this->email = $email;
8 }
9
10 public function __toString() : string {
11 return $this->email;
12 }
13 }
ValueObjects ensure consistency Immutability Validate your data
16. Aggregates
Martin Fowler defines Aggregates as a cluster of domain objects that can be treated as a
single unit.
Aggregates are all about transactions.
1 class Order {
2 function __construct(
3 Customer $customer,
4 ProductList $productList
5 ) {
6 $this->id = Uuid::uuid4();
7 $this->customer = $customer;
8 $this->productList = $productList;
9 $this->status = Status::open();
10 }
11
12 public function getTotal() {
13 // ...
14 }
15
16 // ...
17 }
17. Repositories
1 interface OrderRepository {
2 public function findById($id) : Order;
3
4 public function findByUser(User $user) : ArrayCollection;
5 }
Repositories act as storage locations, where a retrieved object is returned
in the exact same state it was persisted
1 class DoctrineOrderRepository implements OrderRepository { }
2
3 class FileOrderRepository implements OrderRepository { }
4
5 class InMemoryOrderRepository implements OrderRepository { }
Infrastructure Layer
Prefer Query Functions
18. Factories
Factories help in decoupling the client from knowing how to build complex
objects and Aggregates
1 class ComplexObjectFactory {
2
3 public function create (
4 User $user,
5 ProductList $productList,
6 ShipAddress $shipAddress
7 ) {
8 $object = new Object(...);
9
10 // complex logic
11
12 return $object;
13 }
14 }
15
19. Services
When there are operations
that need to be represented,
we can consider them to be
services
1 class OrderCreator {
2
3 /**
4 * @var EntityManagerInterface
5 */
6 private $entityManager;
7
8 /**
9 * @var EventDispatcherInterface
10 */
11 private $eventDispatcher;
12
13 public function create(
14 Customer $customer,
15 ProductList $productList
16 ) : Order {
17 $order = $this->factory->create(...);
18
19 // ... some domain validations
20
21 $this->entityManager->persist($order);
22
23 $this->eventDispatcher->dispatch(
24 OrderEvent::CREATED,
25 new OrderEvent($order)
26 );
27
28 // ...
29
30 return $order;
31 }
32 }
CouponDiscount = null
Prefer NullObject Pattern
20. Command Patter
Ubiquitous Language
1 $command = new SuspendCustomer($customer);
2
3 $commandBus->handle($command);
Commands express intend (user actions)
Commands are simple
Commands are Value Objects
26. Credits and References
Luís Otávio Cobucci Oblonczyk
Domain-driven Design - Eric Evans
Patterns of Enterprise Application Architecture - Martin Folwer
Mathias Verraes
Domain-Driven Design in PHP
Carlos Buenosvinos, Christian Soronellas and Keyvan Akbary
Marco Pivetta
Isabel Janke <3