SlideShare ist ein Scribd-Unternehmen logo
1 von 70
Downloaden Sie, um offline zu lesen
Command-Oriented
Architecture
Maceió DEV Meetup #6
who am I?
➔ Tony Messias ~ @tony0x01
➔ Building web stuff since ~2010
before we start...
➔ CRUD thinking
➔ MVC
➔ Commands/Events
➔ Clean Architecture
“CRUD is an
antipattern”
(Mathias Verraes)
“CRUD doesn't express
behaviour. Avoid setters, and
use expressive, encapsulated
operations instead.”
<?php
$order = new Order();
$order->setStatus('paid');
$order->setPaidAmount(120);
$order->setPaidCurrency('EUR');
$order->setCustomer($customer);
<?php
$order = new Order();
$money = new Money(120, new Currency('EUR'));
$order->pay($customer, $money);
class CommentsController extends Controller {
public function store($postId)
{
$post = Post::find($postId);
$comment = new Comment([
'message' => 'A new comment.',
'user_id' => Auth::user()->id
]);
$post->comments()->save($comment);
return redirect()
->route('posts.view, $post)
->withMessage('Your comment was successfully created');
}
}
class CommentsController extends Controller {
public function store($postId)
{
$post = Post::find($postId);
$comment = new Comment([
'message' => 'A new comment.',
'user_id' => Auth::user()->id
]);
$post->comments()->save($comment);
return redirect()
->route('posts.view, $post)
->withMessage('Your comment was successfully created');
}
}
class CommentsController extends Controller {
public function store($postId)
{
$post = Post::find($postId);
$comment = new Comment(['message' => 'A new comment.']);
$user = Auth::user();
$post->comment($user, $comment);
return redirect()
->route('posts.index', $post)
->withMessage('Your comment was successfully created');
}
}
class CommentsController extends Controller {
public function store($postId)
{
$post = Post::find($postId);
$comment = new Comment(['message' => 'A new comment.']);
$user = Auth::user();
$post->comment($user, $comment);
return redirect()
->route('posts.index', $post)
->withMessage('Your comment was successfully created');
}
}
class Post extends Model
{
// ...
public function comment(User $user, Comment $comment)
{
$comment->user_id = $user->id;
$this->comments()->save($comment);
}
// ...
}
class SendSMS {
public function fire($job, $data)
{
$twilio = new Twilio_SMS($apiKey);
$twilio->sendTextMessage(array(
'to' => $data['user']['phone_number'],
'message' => $data['message'],
));
$user = User::find($data['user']['id']);
$user->messages()->create([
'to' => $data['user']['phone_number'],
'message' => $data['message'],
]);
$job->delete();
}
}
class SendSMS {
public function fire($job, $data)
{
$twilio = new Twilio_SMS($apiKey);
$twilio->sendTextMessage(array(
'to' => $data['user']['phone_number'],
'message' => $data['message'],
));
$user = User::find($data['user']['id']);
$user->messages()->create([
'to' => $data['user']['phone_number'],
'message' => $data['message'],
]);
$job->delete();
}
}
class SendSMS {
public function fire($job, $data)
{
$twilio = new Twilio_SMS($apiKey);
$twilio->sendTextMessage(array(
'to' => $data['user']['phone_number'],
'message' => $data['message'],
));
$user = User::find($data['user']['id']);
$user->messages()->create([
'to' => $data['user']['phone_number'],
'message' => $data['message'],
]);
$job->delete();
}
}
class SendSMS {
function __construct(UserRepository $users, SmsCourierInterface $courier)
{
$this->users = $users;
$this->courier = $courier;
}
public function fire($job, $data)
{
$user = $this->users->find($data['user']['id']);
$user->sendSmsMessage($this->courier, $data['message']);
$job->delete();
}
}
use IlluminateDatabaseEloquentModel;
class User extends Model
{
public function sendSmsMessage(SmsCourierInterface $courier, $message)
{
$courier->sendMessage($this->phone_number, $message);
return $this->messages()->create([
'to' => $this->phone_number,
'message' => $message,
]);
}
}
class SmsTest extends PHPUnit_Framework_TestCase {
public function test_user_can_send_sms_message() {
$user = Mockery::mock('User[messages]');
$relation = Mockery::mock('StdClass');
$courier = Mockery::mock('SmsCourierInterface');
$user->shouldReceive('messages')->once()->andReturn($relation);
$relation->shouldReceive('create')->once()->with(array(
'to' => '555-555-5555',
'message' => 'Test',
));
$courier->shouldReceive('sendMessage')->once()->with(
'555-555-5555', 'Test'
);
$user->phone_number = '555-555-5555';
$user->sendSmsMessage($courier, 'Test');
}
}
class SmsTest extends PHPUnit_Framework_TestCase {
public function test_user_can_send_sms_message() {
$user = Mockery::mock('User[messages]');
$relation = Mockery::mock('StdClass');
$courier = Mockery::mock('SmsCourierInterface');
$user->shouldReceive('messages')->once()->andReturn($relation);
$relation->shouldReceive('create')->once()->with(array(
'to' => '555-555-5555',
'message' => 'Test',
));
$courier->shouldReceive('sendMessage')->once()->with(
'555-555-5555', 'Test'
);
$user->phone_number = '555-555-5555';
$user->sendSmsMessage($courier, 'Test');
}
}
class SmsTest extends PHPUnit_Framework_TestCase {
public function test_user_can_send_sms_message() {
$user = Mockery::mock('User[messages]');
$relation = Mockery::mock('StdClass');
$courier = Mockery::mock('SmsCourierInterface');
$user->shouldReceive('messages')->once()->andReturn($relation);
$relation->shouldReceive('create')->once()->with(array(
'to' => '555-555-5555',
'message' => 'Test',
));
$courier->shouldReceive('sendMessage')->once()->with(
'555-555-5555', 'Test'
);
$user->phone_number = '555-555-5555';
$user->sendSmsMessage($courier, 'Test');
}
}
class SmsTest extends PHPUnit_Framework_TestCase {
public function test_user_can_send_sms_message() {
$user = Mockery::mock('User[messages]');
$relation = Mockery::mock('StdClass');
$courier = Mockery::mock('SmsCourierInterface');
$user->shouldReceive('messages')->once()->andReturn($relation);
$relation->shouldReceive('create')->once()->with(array(
'to' => '555-555-5555',
'message' => 'Test',
));
$courier->shouldReceive('sendMessage')->once()->with(
'555-555-5555', 'Test'
);
$user->phone_number = '555-555-5555';
$user->sendSmsMessage($courier, 'Test');
}
}
be careful with
MVC
your framework is not
your architecture
$ tree rails/app
rails/app
├── assets
├── controllers
├── helpers
├── mailers
├── models
└── views
“this is a rails app”
Screaming Architecture
ok, but what does it have
to do with Commands?
they are basically DTOs,
with cool names
class CommentsController extends Controller
{
public function store($postId)
{
$user = Auth::user();
$post = Post::find($postId);
$comment = new Comment(['message' => 'A new comment.']);
$post->comment($user, $comment);
return redirect()
->route('posts.index', $post)
->withMessage('Your comment was successfully created');
}
}
class CommentsController extends Controller
{
public function store($postId)
{
$user = Auth::user();
$post = Post::find($postId);
$comment = new Comment(['message' => 'A new comment.']);
$post->comment($user, $comment);
return redirect()
->route('posts.index', $post)
->withMessage('Your comment was successfully created');
}
}
class CommentsController extends Controller
{
public function store($postId)
{
$user = Auth::user();
$post = Post::find($postId);
$comment = new Comment(['message' => 'A new comment.']);
$post->comment($user, $comment);
return redirect()
->route('posts.index', $post)
->withMessage('Your comment was successfully created');
}
}
class CommentsController extends Controller
{
public function store($postId)
{
$user = Auth::user();
$message = Input::get('message');
$command = new LeaveCommentCommand($user, $postId, $message);
return redirect()
->route('posts.index', $post)
->withMessage('Your comment was successfully created');
}
}
class LeaveCommentCommand
{
public $user;
public $postId;
public $message;
public function __construct(User $user, $postId, $message)
{
$this->user = $user;
$this->postId = $postId;
$this->message = $message;
}
}
how do I execute them?
class CommentsController extends Controller
{
public function store($postId)
{
$user = Auth::user();
$message = Input::get('message');
$command = new LeaveCommentCommand($user, $postId, $message);
return redirect()
->route('posts.index', $post)
->withMessage('Your comment was successfully created');
}
}
use IlluminateFoundationBusDispatchesCommands;
class CommentsController extends Controller {
use DispatchesCommands;
public function store($postId) {
$user = Auth::user();
$message = Input::get('message');
$command = new LeaveCommentCommand($user, $postId, $message);
$this->dispatch($command);
return redirect()
->route('posts.index', $post)
->withMessage('Your comment was successfully created');
}
}
what does dispatch do?
finds a handler
for our command
one Command can be
executed by one and
only one Handler
LeaveCommentCommand
LeaveCommentCommandHandler
class LeaveCommentCommandHandler
{
public function handle(LeaveCommentCommand $command)
{
$post = Post::find($command->postId);
$comment = new Comment(['message' => $command->message]);
$post->comment($command->user, $comment);
}
}
what if I want to notify
the post creator about
that new comment?
class LeaveCommentCommandHandler {
private $mailer;
function __construct(UserMailer $mailer) {
$this->mailer = $mailer;
}
public function handle(LeaveCommentCommand $command) {
$post = Post::find($command->postId);
$comment = new Comment(['message' => $command->message]);
$post->comment($command->user, $comment);
$this->notifyPostCreator($post->creator, $post, $comment);
}
// ...
}
class LeaveCommentCommandHandler
{
// ...
private function notifyPostCreator(
User $creator, Post $post, Comment $comment)
{
$this->mailer->sendTo(
$creator->email,
sprintf("New comment on [%s]", $post->title),
sprintf("User @%s left a comment for you: n%s",
$comment->user->username,
$comment->message)
);
}
}
works, but we can do
better...
use IlluminateContractsEventsDispatcher;
class LeaveCommentCommandHandler {
private $events;
function __construct(Dispatcher $events) {
$this->events = $events;
}
public function handle(LeaveCommentCommand $command) {
$post = Post::find($command->postId);
$comment = new Comment(['message' => $command->message]);
$post->comment($command->user, $comment);
$this->dispatchEvents($post->releaseEvents());
}
// ...
}
use IlluminateContractsEventsDispatcher;
class LeaveCommentCommandHandler {
// ...
private function dispatchEvents(array $events)
{
foreach ($events as $event)
$this->events->fire($event);
}
}
class Post extends Model
{
use EventGenerator;
public function comment(User $user, Comment $comment)
{
$comment->user_id = $user->id;
$this->comments()->save($comment);
$this->raise(new CommentWasLeft($post, $comment, $user));
}
}
trait EventGenerator
{
protected $domainEvents = [];
public function raise($event)
{
$this->domainEvents[] = $event;
}
public function releaseEvents()
{
$events = $this->domainEvents;
$this->domainEvents = [];
return $events;
}
}
events are also just DTOs
class CommentWasLeft
{
public $post;
public $user;
public $comment;
public function __construct(Post $post, User $user, Comment $comment)
{
$this->post = $post;
$this->user = $user;
$this->comment = $comment;
}
}
but they can (and most
of the time they do)
have lots of
listeners/handlers
class NotifyPostOwnerAboutNewCommentHandler {
private $mailer;
function __construct(UserMailer $mailer) {
$this->mailer = $mailer;
}
public function handle(CommentWasLeft $event) {
$this->mailer->sendTo(
$event->post->creator->email,
sprintf("New comment on [%s]", $event->post->title),
sprintf("User @%s left a comment for you: n%s",
$event->user->username, $event->comment->message)
);
}
}
class EventServiceProvider extends ServiceProvider
{
/**
* The event handler mappings for the application.
* @param array
*/
protected $listen = [
CommentWasLeft::class => [
NotifyPostOwnerAboutNewCommentHandler::class
]
];
}
Recap:
➔ Boundaries interacts through commands;
➔ Command is executed by its handler;
➔ Command handlers fires/triggers domain
events;
➔ Events are listened by event handlers/listeners.
$ tree app
app
├── Commands
├── Console
├── Events
├── Exceptions
├── Handlers
├── Http
├── Providers
├── Services
└── User.php
$ tree app/Commands
app/Commands
├── Command.php
└── LeaveCommentCommand.php
$ tree app/Handlers
app/Handlers
├── Commands
│ └── LeaveCommentCommandHandler.php
└── Events
└── NotifyPostOwnerAboutNewCommentHandler.php
avoid CRUD thinking
$ tree app/Commands
app/Commands
├── CreateUserCommand.php
└── DeleteUserCommand.php
└── UpdateUserCommand.php
class DeactivateInventoryItemCommand
{
public $userId;
public $itemId;
public $comment;
public function __construct($userId, $itemId, $comment)
{
$this->userId = $userId;
$this->itemId = $itemId;
$this->comment = $comment;
}
}
you can easily use
queues to speed up your
requests.
use IlluminateContractsQueueShouldBeQueued;
class DeactivateInventoryItemCommand implements ShouldBeQueued {
public $userId;
public $itemId;
public $comment;
public function __construct($userId, $itemId, $comment) {
$this->userId = $userId;
$this->itemId = $itemId;
$this->comment = $comment;
}
}
use IlluminateContractsQueueShouldBeQueued;
class NotifyPostOwnerAboutNewCommentHandler implements ShouldBeQueued {
private $mailer;
function __construct(UserMailer $mailer) {
$this->mailer = $mailer;
}
public function handle(CommentWasLeft $event) {
$this->mailer->sendTo(
$event->post->creator->email,
sprintf("New comment on [%s]", $event->post->title),
sprintf("User @%s left a comment for you: n%s",
$event->user->username, $event->comment->message)
);
}
}
questions?
Resources
➔ Command Bus by Shawn Mccool
➔ Dev Discussions - The Command Bus
➔ Screaming Archirecture by Uncle Bob
➔ The Clean Archirecture by Uncle Bob
➔ Laravel: From Apprentice to Artisan
Resources
➔ Commands and Domain Events
(Laracasts)
➔ Task-based UIs
➔ CRUD is an antipattern by Mathias
Verraes

Weitere ähnliche Inhalte

Was ist angesagt?

Kick start with j query
Kick start with j queryKick start with j query
Kick start with j queryMd. Ziaul Haq
 
jQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
jQuery UI Widgets, Drag and Drop, Drupal 7 JavascriptjQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
jQuery UI Widgets, Drag and Drop, Drupal 7 JavascriptDarren Mothersele
 
Cleaner, Leaner, Meaner: Refactoring your jQuery
Cleaner, Leaner, Meaner: Refactoring your jQueryCleaner, Leaner, Meaner: Refactoring your jQuery
Cleaner, Leaner, Meaner: Refactoring your jQueryRebecca Murphey
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code OrganizationRebecca Murphey
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Leonardo Proietti
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 
Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Colin O'Dell
 
Object Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPObject Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPChad Gray
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mockingKonstantin Kudryashov
 
Miniproject on Employee Management using Perl/Database.
Miniproject on Employee Management using Perl/Database.Miniproject on Employee Management using Perl/Database.
Miniproject on Employee Management using Perl/Database.Sanchit Raut
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuerysergioafp
 
WordPress as an application framework
WordPress as an application frameworkWordPress as an application framework
WordPress as an application frameworkDustin Filippini
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteLeonardo Proietti
 
JQuery In Rails
JQuery In RailsJQuery In Rails
JQuery In RailsLouie Zhao
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConRafael Dohms
 

Was ist angesagt? (20)

Kick start with j query
Kick start with j queryKick start with j query
Kick start with j query
 
jQuery secrets
jQuery secretsjQuery secrets
jQuery secrets
 
jQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
jQuery UI Widgets, Drag and Drop, Drupal 7 JavascriptjQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
jQuery UI Widgets, Drag and Drop, Drupal 7 Javascript
 
Cleaner, Leaner, Meaner: Refactoring your jQuery
Cleaner, Leaner, Meaner: Refactoring your jQueryCleaner, Leaner, Meaner: Refactoring your jQuery
Cleaner, Leaner, Meaner: Refactoring your jQuery
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code Organization
 
BVJS
BVJSBVJS
BVJS
 
Presentation1
Presentation1Presentation1
Presentation1
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
 
Separation of concerns - DPC12
Separation of concerns - DPC12Separation of concerns - DPC12
Separation of concerns - DPC12
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016Hacking Your Way To Better Security - Dutch PHP Conference 2016
Hacking Your Way To Better Security - Dutch PHP Conference 2016
 
Object Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPObject Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHP
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Miniproject on Employee Management using Perl/Database.
Miniproject on Employee Management using Perl/Database.Miniproject on Employee Management using Perl/Database.
Miniproject on Employee Management using Perl/Database.
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuery
 
WordPress as an application framework
WordPress as an application frameworkWordPress as an application framework
WordPress as an application framework
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
 
JQuery In Rails
JQuery In RailsJQuery In Rails
JQuery In Rails
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnCon
 
jQuery
jQueryjQuery
jQuery
 

Ähnlich wie Command-Oriented Architecture

Php update and delet operation
Php update and delet operationPhp update and delet operation
Php update and delet operationsyeda zoya mehdi
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency InjectionRifat Nabi
 
Jak neopakovat kód, ale nepo**** abstrakci | Jiří Pudil | 15. 2. 2023 – Kiwi.com
Jak neopakovat kód, ale nepo**** abstrakci | Jiří Pudil | 15. 2. 2023 – Kiwi.comJak neopakovat kód, ale nepo**** abstrakci | Jiří Pudil | 15. 2. 2023 – Kiwi.com
Jak neopakovat kód, ale nepo**** abstrakci | Jiří Pudil | 15. 2. 2023 – Kiwi.comWebScience1
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan Subedi
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutesBarang CK
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 MinutesAzim Kurt
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hackingJeroen van Dijk
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From IusethisMarcus Ramberg
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your CodeAbbas Ali
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleHugo Hamon
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web servicesMichelangelo van Dam
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of LithiumNate Abele
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011Alessandro Nadalin
 

Ähnlich wie Command-Oriented Architecture (20)

Php update and delet operation
Php update and delet operationPhp update and delet operation
Php update and delet operation
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
 
Jak neopakovat kód, ale nepo**** abstrakci | Jiří Pudil | 15. 2. 2023 – Kiwi.com
Jak neopakovat kód, ale nepo**** abstrakci | Jiří Pudil | 15. 2. 2023 – Kiwi.comJak neopakovat kód, ale nepo**** abstrakci | Jiří Pudil | 15. 2. 2023 – Kiwi.com
Jak neopakovat kód, ale nepo**** abstrakci | Jiří Pudil | 15. 2. 2023 – Kiwi.com
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From Iusethis
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your Code
 
PHP || [Student Result Management System]
PHP || [Student Result Management System]PHP || [Student Result Management System]
PHP || [Student Result Management System]
 
Bacbkone js
Bacbkone jsBacbkone js
Bacbkone js
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
 
The Zen of Lithium
The Zen of LithiumThe Zen of Lithium
The Zen of Lithium
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011 Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
Be lazy, be ESI: HTTP caching and Symfony2 @ PHPDay 2011 05-13-2011
 

Mehr von Luiz Messias

Phoenix for laravel developers
Phoenix for laravel developersPhoenix for laravel developers
Phoenix for laravel developersLuiz Messias
 
Queues & Async Apps
 Queues & Async Apps Queues & Async Apps
Queues & Async AppsLuiz Messias
 
Laravel's ecosystem
Laravel's ecosystemLaravel's ecosystem
Laravel's ecosystemLuiz Messias
 
Introduction to Elasticsearch
Introduction to ElasticsearchIntroduction to Elasticsearch
Introduction to ElasticsearchLuiz Messias
 
APIs seguras com OAuth2
APIs seguras com OAuth2APIs seguras com OAuth2
APIs seguras com OAuth2Luiz Messias
 
Google App Engine e PHP
Google App Engine e PHPGoogle App Engine e PHP
Google App Engine e PHPLuiz Messias
 

Mehr von Luiz Messias (7)

Phoenix for laravel developers
Phoenix for laravel developersPhoenix for laravel developers
Phoenix for laravel developers
 
Turbolinks
TurbolinksTurbolinks
Turbolinks
 
Queues & Async Apps
 Queues & Async Apps Queues & Async Apps
Queues & Async Apps
 
Laravel's ecosystem
Laravel's ecosystemLaravel's ecosystem
Laravel's ecosystem
 
Introduction to Elasticsearch
Introduction to ElasticsearchIntroduction to Elasticsearch
Introduction to Elasticsearch
 
APIs seguras com OAuth2
APIs seguras com OAuth2APIs seguras com OAuth2
APIs seguras com OAuth2
 
Google App Engine e PHP
Google App Engine e PHPGoogle App Engine e PHP
Google App Engine e PHP
 

Kürzlich hochgeladen

Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commercemanigoyal112
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfLivetecs LLC
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...confluent
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Natan Silnitsky
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfMarharyta Nedzelska
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentationvaddepallysandeep122
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfStefano Stabellini
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noidabntitsolutionsrishis
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 

Kürzlich hochgeladen (20)

Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commerce
 
Advantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your BusinessAdvantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your Business
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdf
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdf
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentation
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdf
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 

Command-Oriented Architecture

  • 2. who am I? ➔ Tony Messias ~ @tony0x01 ➔ Building web stuff since ~2010
  • 3.
  • 4. before we start... ➔ CRUD thinking ➔ MVC ➔ Commands/Events ➔ Clean Architecture
  • 6. “CRUD doesn't express behaviour. Avoid setters, and use expressive, encapsulated operations instead.”
  • 7. <?php $order = new Order(); $order->setStatus('paid'); $order->setPaidAmount(120); $order->setPaidCurrency('EUR'); $order->setCustomer($customer);
  • 8. <?php $order = new Order(); $money = new Money(120, new Currency('EUR')); $order->pay($customer, $money);
  • 9. class CommentsController extends Controller { public function store($postId) { $post = Post::find($postId); $comment = new Comment([ 'message' => 'A new comment.', 'user_id' => Auth::user()->id ]); $post->comments()->save($comment); return redirect() ->route('posts.view, $post) ->withMessage('Your comment was successfully created'); } }
  • 10. class CommentsController extends Controller { public function store($postId) { $post = Post::find($postId); $comment = new Comment([ 'message' => 'A new comment.', 'user_id' => Auth::user()->id ]); $post->comments()->save($comment); return redirect() ->route('posts.view, $post) ->withMessage('Your comment was successfully created'); } }
  • 11. class CommentsController extends Controller { public function store($postId) { $post = Post::find($postId); $comment = new Comment(['message' => 'A new comment.']); $user = Auth::user(); $post->comment($user, $comment); return redirect() ->route('posts.index', $post) ->withMessage('Your comment was successfully created'); } }
  • 12. class CommentsController extends Controller { public function store($postId) { $post = Post::find($postId); $comment = new Comment(['message' => 'A new comment.']); $user = Auth::user(); $post->comment($user, $comment); return redirect() ->route('posts.index', $post) ->withMessage('Your comment was successfully created'); } }
  • 13. class Post extends Model { // ... public function comment(User $user, Comment $comment) { $comment->user_id = $user->id; $this->comments()->save($comment); } // ... }
  • 14. class SendSMS { public function fire($job, $data) { $twilio = new Twilio_SMS($apiKey); $twilio->sendTextMessage(array( 'to' => $data['user']['phone_number'], 'message' => $data['message'], )); $user = User::find($data['user']['id']); $user->messages()->create([ 'to' => $data['user']['phone_number'], 'message' => $data['message'], ]); $job->delete(); } }
  • 15. class SendSMS { public function fire($job, $data) { $twilio = new Twilio_SMS($apiKey); $twilio->sendTextMessage(array( 'to' => $data['user']['phone_number'], 'message' => $data['message'], )); $user = User::find($data['user']['id']); $user->messages()->create([ 'to' => $data['user']['phone_number'], 'message' => $data['message'], ]); $job->delete(); } }
  • 16. class SendSMS { public function fire($job, $data) { $twilio = new Twilio_SMS($apiKey); $twilio->sendTextMessage(array( 'to' => $data['user']['phone_number'], 'message' => $data['message'], )); $user = User::find($data['user']['id']); $user->messages()->create([ 'to' => $data['user']['phone_number'], 'message' => $data['message'], ]); $job->delete(); } }
  • 17. class SendSMS { function __construct(UserRepository $users, SmsCourierInterface $courier) { $this->users = $users; $this->courier = $courier; } public function fire($job, $data) { $user = $this->users->find($data['user']['id']); $user->sendSmsMessage($this->courier, $data['message']); $job->delete(); } }
  • 18. use IlluminateDatabaseEloquentModel; class User extends Model { public function sendSmsMessage(SmsCourierInterface $courier, $message) { $courier->sendMessage($this->phone_number, $message); return $this->messages()->create([ 'to' => $this->phone_number, 'message' => $message, ]); } }
  • 19. class SmsTest extends PHPUnit_Framework_TestCase { public function test_user_can_send_sms_message() { $user = Mockery::mock('User[messages]'); $relation = Mockery::mock('StdClass'); $courier = Mockery::mock('SmsCourierInterface'); $user->shouldReceive('messages')->once()->andReturn($relation); $relation->shouldReceive('create')->once()->with(array( 'to' => '555-555-5555', 'message' => 'Test', )); $courier->shouldReceive('sendMessage')->once()->with( '555-555-5555', 'Test' ); $user->phone_number = '555-555-5555'; $user->sendSmsMessage($courier, 'Test'); } }
  • 20. class SmsTest extends PHPUnit_Framework_TestCase { public function test_user_can_send_sms_message() { $user = Mockery::mock('User[messages]'); $relation = Mockery::mock('StdClass'); $courier = Mockery::mock('SmsCourierInterface'); $user->shouldReceive('messages')->once()->andReturn($relation); $relation->shouldReceive('create')->once()->with(array( 'to' => '555-555-5555', 'message' => 'Test', )); $courier->shouldReceive('sendMessage')->once()->with( '555-555-5555', 'Test' ); $user->phone_number = '555-555-5555'; $user->sendSmsMessage($courier, 'Test'); } }
  • 21. class SmsTest extends PHPUnit_Framework_TestCase { public function test_user_can_send_sms_message() { $user = Mockery::mock('User[messages]'); $relation = Mockery::mock('StdClass'); $courier = Mockery::mock('SmsCourierInterface'); $user->shouldReceive('messages')->once()->andReturn($relation); $relation->shouldReceive('create')->once()->with(array( 'to' => '555-555-5555', 'message' => 'Test', )); $courier->shouldReceive('sendMessage')->once()->with( '555-555-5555', 'Test' ); $user->phone_number = '555-555-5555'; $user->sendSmsMessage($courier, 'Test'); } }
  • 22. class SmsTest extends PHPUnit_Framework_TestCase { public function test_user_can_send_sms_message() { $user = Mockery::mock('User[messages]'); $relation = Mockery::mock('StdClass'); $courier = Mockery::mock('SmsCourierInterface'); $user->shouldReceive('messages')->once()->andReturn($relation); $relation->shouldReceive('create')->once()->with(array( 'to' => '555-555-5555', 'message' => 'Test', )); $courier->shouldReceive('sendMessage')->once()->with( '555-555-5555', 'Test' ); $user->phone_number = '555-555-5555'; $user->sendSmsMessage($courier, 'Test'); } }
  • 24. your framework is not your architecture
  • 25. $ tree rails/app rails/app ├── assets ├── controllers ├── helpers ├── mailers ├── models └── views
  • 26. “this is a rails app”
  • 28. ok, but what does it have to do with Commands?
  • 29.
  • 30. they are basically DTOs, with cool names
  • 31. class CommentsController extends Controller { public function store($postId) { $user = Auth::user(); $post = Post::find($postId); $comment = new Comment(['message' => 'A new comment.']); $post->comment($user, $comment); return redirect() ->route('posts.index', $post) ->withMessage('Your comment was successfully created'); } }
  • 32. class CommentsController extends Controller { public function store($postId) { $user = Auth::user(); $post = Post::find($postId); $comment = new Comment(['message' => 'A new comment.']); $post->comment($user, $comment); return redirect() ->route('posts.index', $post) ->withMessage('Your comment was successfully created'); } }
  • 33. class CommentsController extends Controller { public function store($postId) { $user = Auth::user(); $post = Post::find($postId); $comment = new Comment(['message' => 'A new comment.']); $post->comment($user, $comment); return redirect() ->route('posts.index', $post) ->withMessage('Your comment was successfully created'); } }
  • 34. class CommentsController extends Controller { public function store($postId) { $user = Auth::user(); $message = Input::get('message'); $command = new LeaveCommentCommand($user, $postId, $message); return redirect() ->route('posts.index', $post) ->withMessage('Your comment was successfully created'); } }
  • 35. class LeaveCommentCommand { public $user; public $postId; public $message; public function __construct(User $user, $postId, $message) { $this->user = $user; $this->postId = $postId; $this->message = $message; } }
  • 36. how do I execute them?
  • 37. class CommentsController extends Controller { public function store($postId) { $user = Auth::user(); $message = Input::get('message'); $command = new LeaveCommentCommand($user, $postId, $message); return redirect() ->route('posts.index', $post) ->withMessage('Your comment was successfully created'); } }
  • 38. use IlluminateFoundationBusDispatchesCommands; class CommentsController extends Controller { use DispatchesCommands; public function store($postId) { $user = Auth::user(); $message = Input::get('message'); $command = new LeaveCommentCommand($user, $postId, $message); $this->dispatch($command); return redirect() ->route('posts.index', $post) ->withMessage('Your comment was successfully created'); } }
  • 40. finds a handler for our command
  • 41. one Command can be executed by one and only one Handler
  • 43. class LeaveCommentCommandHandler { public function handle(LeaveCommentCommand $command) { $post = Post::find($command->postId); $comment = new Comment(['message' => $command->message]); $post->comment($command->user, $comment); } }
  • 44. what if I want to notify the post creator about that new comment?
  • 45. class LeaveCommentCommandHandler { private $mailer; function __construct(UserMailer $mailer) { $this->mailer = $mailer; } public function handle(LeaveCommentCommand $command) { $post = Post::find($command->postId); $comment = new Comment(['message' => $command->message]); $post->comment($command->user, $comment); $this->notifyPostCreator($post->creator, $post, $comment); } // ... }
  • 46. class LeaveCommentCommandHandler { // ... private function notifyPostCreator( User $creator, Post $post, Comment $comment) { $this->mailer->sendTo( $creator->email, sprintf("New comment on [%s]", $post->title), sprintf("User @%s left a comment for you: n%s", $comment->user->username, $comment->message) ); } }
  • 47. works, but we can do better...
  • 48. use IlluminateContractsEventsDispatcher; class LeaveCommentCommandHandler { private $events; function __construct(Dispatcher $events) { $this->events = $events; } public function handle(LeaveCommentCommand $command) { $post = Post::find($command->postId); $comment = new Comment(['message' => $command->message]); $post->comment($command->user, $comment); $this->dispatchEvents($post->releaseEvents()); } // ... }
  • 49. use IlluminateContractsEventsDispatcher; class LeaveCommentCommandHandler { // ... private function dispatchEvents(array $events) { foreach ($events as $event) $this->events->fire($event); } }
  • 50. class Post extends Model { use EventGenerator; public function comment(User $user, Comment $comment) { $comment->user_id = $user->id; $this->comments()->save($comment); $this->raise(new CommentWasLeft($post, $comment, $user)); } }
  • 51. trait EventGenerator { protected $domainEvents = []; public function raise($event) { $this->domainEvents[] = $event; } public function releaseEvents() { $events = $this->domainEvents; $this->domainEvents = []; return $events; } }
  • 52. events are also just DTOs
  • 53. class CommentWasLeft { public $post; public $user; public $comment; public function __construct(Post $post, User $user, Comment $comment) { $this->post = $post; $this->user = $user; $this->comment = $comment; } }
  • 54. but they can (and most of the time they do) have lots of listeners/handlers
  • 55. class NotifyPostOwnerAboutNewCommentHandler { private $mailer; function __construct(UserMailer $mailer) { $this->mailer = $mailer; } public function handle(CommentWasLeft $event) { $this->mailer->sendTo( $event->post->creator->email, sprintf("New comment on [%s]", $event->post->title), sprintf("User @%s left a comment for you: n%s", $event->user->username, $event->comment->message) ); } }
  • 56. class EventServiceProvider extends ServiceProvider { /** * The event handler mappings for the application. * @param array */ protected $listen = [ CommentWasLeft::class => [ NotifyPostOwnerAboutNewCommentHandler::class ] ]; }
  • 57. Recap: ➔ Boundaries interacts through commands; ➔ Command is executed by its handler; ➔ Command handlers fires/triggers domain events; ➔ Events are listened by event handlers/listeners.
  • 58. $ tree app app ├── Commands ├── Console ├── Events ├── Exceptions ├── Handlers ├── Http ├── Providers ├── Services └── User.php
  • 59. $ tree app/Commands app/Commands ├── Command.php └── LeaveCommentCommand.php $ tree app/Handlers app/Handlers ├── Commands │ └── LeaveCommentCommandHandler.php └── Events └── NotifyPostOwnerAboutNewCommentHandler.php
  • 61. $ tree app/Commands app/Commands ├── CreateUserCommand.php └── DeleteUserCommand.php └── UpdateUserCommand.php
  • 62.
  • 63.
  • 64. class DeactivateInventoryItemCommand { public $userId; public $itemId; public $comment; public function __construct($userId, $itemId, $comment) { $this->userId = $userId; $this->itemId = $itemId; $this->comment = $comment; } }
  • 65. you can easily use queues to speed up your requests.
  • 66. use IlluminateContractsQueueShouldBeQueued; class DeactivateInventoryItemCommand implements ShouldBeQueued { public $userId; public $itemId; public $comment; public function __construct($userId, $itemId, $comment) { $this->userId = $userId; $this->itemId = $itemId; $this->comment = $comment; } }
  • 67. use IlluminateContractsQueueShouldBeQueued; class NotifyPostOwnerAboutNewCommentHandler implements ShouldBeQueued { private $mailer; function __construct(UserMailer $mailer) { $this->mailer = $mailer; } public function handle(CommentWasLeft $event) { $this->mailer->sendTo( $event->post->creator->email, sprintf("New comment on [%s]", $event->post->title), sprintf("User @%s left a comment for you: n%s", $event->user->username, $event->comment->message) ); } }
  • 69. Resources ➔ Command Bus by Shawn Mccool ➔ Dev Discussions - The Command Bus ➔ Screaming Archirecture by Uncle Bob ➔ The Clean Archirecture by Uncle Bob ➔ Laravel: From Apprentice to Artisan
  • 70. Resources ➔ Commands and Domain Events (Laracasts) ➔ Task-based UIs ➔ CRUD is an antipattern by Mathias Verraes