The document discusses different software architectural patterns for managing complexity in web applications. It begins by explaining model-view-controller (MVC) and some of its limitations. It then introduces Action Domain Responder (ADR) as a refinement of MVC that maps more closely to the lifecycle of a web request. Next, it covers hexagonal architecture, also known as ports and adapters, which isolates an application from its inputs and outputs. Finally, it provides an overview of domain-driven design (DDD), including architectural patterns like bounded contexts and object patterns like entities, value objects, domain services, aggregates, and repositories.
8. Problems with ‘web’ MVC
❖ Use of the term ‘Model’ is an issue…
❖ Easy to muddy responsibilities in the controller
❖ Offers no guidance in modelling the most critical part of
our apps: the model or domain
12. What is Action Domain Responder?
❖ Created by Paul M Jones
❖ Web specific refinement of MVC
❖ Designed to map more closely to what actually happens
in the lifecycle of a web request
13. What is Action Domain Responder?
❖ Model
❖ View
❖ Controller
14. What is Action Domain Responder?
❖ Controller
❖ Model
❖ View
15. What is Action Domain Responder?
❖ Controller Action
❖ Model Domain
❖ View Responder
16. Action
❖ In ADR an action is mapped to a single class or closure
❖ Creates a single place for all code dealing with an action
❖ Does not directly create a view or HTTP response
17. Responder
❖ ADR recognizes that a web response is more than a
view
❖ Action instantiates a responder object and injects
domain data in it
❖ Responder is responsible for generating all aspects of
the response
18. Domain
❖ ADR offers no help on modelling a domain…
❖ …but at least Paul calls it a domain!
❖ Other patterns later will offer more help here
20. ADR Example
<?php
class FooAction implements ActionInterface {
protected $responderFactory;
public function __construct(ResponderFactoryInterface $factory) {
$this->responderFactory = $factory;
}
public function __invoke(RequestInterface $request) {
//Domain logic here
$responder = $this->responderFactory->generateForRequest($request);
return $responder();
}
}
24. What’s wrong with ‘Hexagonal Architecture’?
❖ Has nothing to do with hexagons
❖ Has nothing to do with the number 6
❖ Ports and adapters describes what this actually does
25. What are Ports and Adapters?
❖ Invented by Alistair Cockburn
❖ Architectural pattern for isolating an application from
its inputs.
26. –Alistair Cockburn
“Allow an application to equally be driven by
users, programs, automated test or batch scripts,
and to be developed and tested in isolation from
its eventual run-time devices and databases.”
27. –Alistair Cockburn
“Allow an application to equally be driven by
users, programs, automated test or batch scripts,
and to be developed and tested in isolation from
its eventual run-time devices and databases.”
32. HTTP Adapter
<?php
class FooAction implements ActionInterface {
protected $responderFactory;
protected $service;
public function __construct(ResponderFactoryInterface $factory,
ApplicationServiceInterface $service) {
$this->responderFactory = $factory;
$this->service = $service;
}
public function __invoke(RequestInterface $request) {
$result = $this->service->__invoke($request->getParamsArray());
$responder = $this->responderFactory->generateForRequest($request);
return $responder($result);
}
}
33. AMQP Adapter
<?php
class FooConsumer implements AMQPConsumer {
protected $service;
public function __construct(ApplicationServiceInterface $service) {
$this->service = $service;
}
public function __invoke(AMQPMessage $msg) {
$data = json_decode($msg->body, true);
$result = $this->service->__invoke($data);
if ($result->isStatusOk()) {
$msg->delivery_info['channel']
->basic_ack($msg->delivery_info['delivery_tag']);
}
}
}
34. Advantages of Ports and Adapters
❖ Encapsulation of the domain
❖ Allows development of the domain and adapters to
be independent
❖ Allows better end to end testability of the domain
❖ Makes it simple to add new entry points to an app
35. Further information on Ports and Adapters
❖ The original article introducing ports and adapters:
http://alistair.cockburn.us/Hexagonal+architecture
37. What is Domain Driven Design?
❖ Concept coined by Eric Evans
❖ Ideas can be grouped into two related areas:
❖ Architectural patterns
❖ Object patterns
42. Ubiquitous language
❖ Develop a shared language to describe each domain
and model the software after it
❖ The domain should be an expression of the ubiquitous
language in code
❖ Aim is to unite all members of a product team, from
developers to product managers
43. Bounded contexts
❖ Represents the boundary around a domain
❖ All code within a domain is completely encapsulated
with specific entry points
❖ Code and concepts are unique per domain
44. Context map
❖ Describes relationships between domains
❖ Outlines all points of contact between domains
❖ Can also include existing systems
46. Entities
❖ Represents a concept in your domain
❖ Each instance of a entity should be considered unique
❖ Houses logic and operation on the concept
47. Value Objects
❖ Used when you only care about the attributes and logic
of the concept
❖ Immutable
❖ All methods must be side effect free
❖ Mutator methods must return a new instance of the
value object
❖ Prefer value objects over simple types
48. Value Objects
<?php
class Employee {
public function __construct($firstName, $lastName) {
//Code here
}
}
$person = new Employee('Cook', 'Jeremy'); //WHOOPS!
49. Value Objects
<?php
class Firstname {
protected $firstname;
public function __construct($firstname) {
if (! is_string($firstname)) {
throw new InvalidArgumentException(/**ErrorMessage**/);
}
$this->firstname = $firstname;
}
public function __toString() {
return $this->firstname;
}
}
50. Value Objects
<?php
class Employee {
public function __construct(Firstname $firstName, Lastname $lastName) {
//Code here
}
}
//This will now cause an error
$person = new Employee(new Lastname('Cook'), new Firstname('Jeremy'));
51. Entities vs Value Objects
!==
Bank Account
Owner: Jeremy
Balance: $1,000,000
Account #: 12345
===
Currency
Name: Canadian Dollar
Abbr: CAD
Symbol: $
Currency
Name: Canadian Dollar
Abbr: CAD
Symbol: $
Bank Account
Owner: Jeremy
Balance: $1,000,000
Account #: 12346
52. Domain Services
❖ Encapsulates an operation that is not owned by another
part of the domain model
❖ A good service has three properties:
❖ Models a domain concept that does not conceptually
belong to an entity or value object
❖ Stateless
❖ Defined in terms of the domain model
53. Domain Services
<?php
class MoneyTransfer {
public static function transferBetweenAccounts(Account $from, Account
$to, Money $amount) {
$from->debitForTransfer($money, $to);
$to->creditFromTransfer($money, $from);
}
}
//In the application service...
$from = $accountRepository->findByAccNumber('123456');
$to = $accountRepository->findByAccNumber('123457');
$money = new Money('100', new Currency('CAD'));
MoneyTransfer::transferBetweenAccounts($from, $to, $money);
54. Aggregates
❖ A collection of entities contained by another entity
❖ The containing entity is the aggregate root
❖ Individual aggregate instances can only be accessed
through the aggregate root
55. Repositories
❖ A specialised adapter that maps entities to and from the
persistence layer
❖ Provides methods to retrieve entities and save them
❖ Abstracts the details of the persistence layer away from
the domain
56. Domain Events
❖ Allows a domain to signal that something as happened
❖ Full part of the domain and a representation of
something that has happened
❖ Used to signal something that might trigger a state
change in another domain
57. Domain Events
$event = EventFactory::createEventForSuccessfulAccountTransfer($from, $to,
$money);
$eventType = EventTypes::SUCCESSFUL_MONEY_TRANSFER;
$eventDispatcher->raiseEvent($eventType, $event);
<?php
class MoneyTransfer {
public static function transferBetweenAccounts(Account $from, Account
$to, Money $amount) {
$from->debitForTransfer($money, $to);
$to->creditFromTransfer($money, $from);
}
}
//In the application service...
$from = $accountRepository->findByAccNumber('123456');
$to = $accountRepository->findByAccNumber('123457');
$money = new Money('100', new Currency('CAD'));
MoneyTransfer::transferBetweenAccounts($from, $to, $money);
58. Further Information on DDD
❖ Eric Evans
❖ “Domain Driven Design” book
❖ Short introduction: https://domainlanguage.com/
ddd/patterns/DDD_Reference_2011-01-31.pdf
❖ Implementing Domain Driven Design by Vaughn
Vernon
60. Thanks for listening!
❖ Any questions?
❖ I’d love some feedback
❖ Feel free to contact me
❖ jeremycook0@icloud.com
❖ @JCook21
Notes de l'éditeur
This as an introductory talk.
I’m going to introduce a number of architectural and structural patterns that can help you model your business domains better.
There is no single bullet solution…
Begin with some level setting
Dictionary definition
Domain include all code needed to model a particular business problem.
Domain layer in an app can imply encapsulation.
I’m going to argue that encapsulating a domain is something we should strive for…
I want to talk about what MVC historically was, why it was adapted for the web and what the compromises are when using it.
MVC for the web is a corrupted pattern…
MVC first developed in the 70’s and 80’s by Trygve Reenskaug, Jim Althoff and others
MVC as it’s generally used on the web.
MVC is about separation of concerns. Without care domain and view logic leaks into controllers, which breaks SOC.
We tend to think of a model (singular). Controller often becomes the place where data is marshalled between models.
View is also controlled by the controller.
Modern web apps are about far more than just user interfaces.
Throws the focus on the UI when mostly now it’s about the data that’s stored. Apps are as much about the data and how it is represented than the UI itself.
All of the solutions today can increase the complexity of your codebase. This should set your alarm bells ringing!!
It’s about exchanging unmanageable complexity for managed complexity.
As requirements grow and change code can easily become unmanageable. This is when you need to move beyond MVC.
Straight up: ADR is not a solution to the problem I’m looking at but a different way of looking at the problem.
ADR helps us to get back to more easily separating concerns.
Each of the three concepts implies a subtle shift in thinking.
We’re used to controllers with many actions.
Additional wrapper logic could be pre or post action hooks.
Allows the action to assume all responsibility for a single action.
Web response is more than a view!
Many MVC frameworks have controller manipulating elements of the response.
Responder is responsible for deciding the HTTP status code, setting headers, etc as well as generating the response body
Could wrap this in a decorator if want before/after hooks
Hopefully ADR has whetted your appetite.
If I say hexagonal architecture what do you think of?
Nevertheless, I will use hexagons and maybe the term hexagonal architecture in this talk!
This quote describes the intent of the pattern.
Database is an implementation detail.
Don’t build your objects to simply ‘sit on top of’ database tables but allow them to be mapped onto a persistence medium.
Think firstly in terms of object relationships.
This is a core tenant of DDD will be elaborated on later…
Move to next slide!
Adapters sit inside the application boundary but not inside the domain boundary
Adapter shuffles data between the input type and the domain, also dealing with a response.
Adapters and domain are only lightly coupled.
The domain has no idea about the types of input and output. Adapters have no idea how the domain is implemented.
If a domain is properly encapsulated then a controller/action is an adapter…
DDD places the design of the domain at the heart of the application development.
Nothing new but codifies best practices.
I can only scratch the surface of this today…
I’ll try to introduce some of the most important concepts.
Blue book: theoretical.
Red book: practical
Really architectural best practices
Talk about core and supporting domains.
Introduce idea of a domain model, the concept of the code model of the domain.
Using different language between dev’s and domain experts leads to misunderstanding and communication failures.
Intention revealing interface
A change in the ubiquitous language is a change to the model.
Ubiquitous language is unique to a bounded context/domain.
Bounded contexts allow the app to be split up.
Code is (almost) never shared between domains
No domain exists in isolation
Upstream and downstream domains, mutually dependent domains.
2 instances of the same entity with the same attributes are not the same!
Avoid anemic domain entities.
Can include data from more than one table!
Also represents a concept in your domain…
Value objects should be as common as entities, if not more so, in your domain.
Mutator/side effect free method example: DateTimeImmutable::modify()
Equality of value objects must be calculated based on their attributes, not identity
Side effect free function produces output without modifying its own state.
Entities can have value objects as properties
It’s all about identity.
Different from application service…
Name of the service should be part of the ubiquitous language
Example of moving money between two bank accounts (entities). Transfer is not owned by either account.
Give an example of aggregate roots and aggregates, e.g. leagues -> seasons -> games -> game events
Rationale: database access code can overwhelm domain model. Leads to dumbed down model, the opposite of what we want.
Just querying for individual fields does not express anything about the domain model, again watering this down.
Repository presents a very simple interface for an incredibly complex operation!
Look at patterns such as: data mapper, unit of work, query object
Mention overhead in dealing with multiple domains
Mention synchronous and asynchronous events
Need to consider the question of what information needs to be consistent immediately across domains and what can wait.