Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Evoluindo sua aplicação - Arquitetura Hexagonal
Search
Hugo Henrique
October 10, 2017
Programming
0
290
Evoluindo sua aplicação - Arquitetura Hexagonal
Apresentado no dia 08/10/2017 no PHPeste em Fortaleza-CE
Hugo Henrique
October 10, 2017
Tweet
Share
More Decks by Hugo Henrique
See All by Hugo Henrique
CommandBus
hugohenrique
0
60
Background Jobs - Ao infinito e além
hugohenrique
1
190
Other Decks in Programming
See All in Programming
ふつうの技術スタックでアート作品を作ってみる
akira888
1
900
設計やレビューに悩んでいるPHPerに贈る、クリーンなオブジェクト設計の指針たち
panda_program
6
2.2k
LT 2025-06-30: プロダクトエンジニアの役割
yamamotok
0
790
『自分のデータだけ見せたい!』を叶える──Laravel × Casbin で複雑権限をスッキリ解きほぐす 25 分
akitotsukahara
2
640
オンコール⼊⾨〜ページャーが鳴る前に、あなたが備えられること〜 / Before The Pager Rings
yktakaha4
0
130
ISUCON研修おかわり会 講義スライド
arfes0e2b3c
1
450
MDN Web Docs に日本語翻訳でコントリビュートしたくなる
ohmori_yusuke
1
130
Startups on Rails in Past, Present and Future–Irina Nazarova, RailsConf 2025
irinanazarova
0
140
코딩 에이전트 체크리스트: Claude Code ver.
nacyot
0
690
#QiitaBash MCPのセキュリティ
ryosukedtomita
1
1.4k
Agentic Coding: The Future of Software Development with Agents
mitsuhiko
0
110
NPOでのDevinの活用
codeforeveryone
0
860
Featured
See All Featured
How to Think Like a Performance Engineer
csswizardry
25
1.7k
Writing Fast Ruby
sferik
628
62k
Build The Right Thing And Hit Your Dates
maggiecrowley
36
2.8k
Fireside Chat
paigeccino
37
3.5k
Reflections from 52 weeks, 52 projects
jeffersonlam
351
20k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
10
970
The Power of CSS Pseudo Elements
geoffreycrofte
77
5.9k
The World Runs on Bad Software
bkeepers
PRO
69
11k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
18
980
Making the Leap to Tech Lead
cromwellryan
134
9.4k
Thoughts on Productivity
jonyablonski
69
4.7k
4 Signs Your Business is Dying
shpigford
184
22k
Transcript
&70-6*/%046""1-*$"ª§0 "326*5&563")&9"(0/"- !IVHPIFOSJRVF
)6(0)&/3*26& !IVHPIFOSJRVF
"4"1-*$"ª¸&4/03."-.&/5& $0.&ª".&..6/%01&3'&*50
None
$&/¥3*0$0.6.%"$3*"ª§0%&6.40'58"3& 4&(6/%00.6/%0%"445"35614
.7$
None
4$3*15.6*50$0.6.1"3"*/*$*"36.130+&50 %&40'58"3&/0$&/¥3*045"3561 &4$0-)&36.'3".&803, */45"-"36.130+&50&426&-&50 3&.07&3$0*4"4%&%&.0/453"ª§0 $3*"3&/5*%"%&4
"650(&/&3"5& $3*"3$36%$0/530--&3`4 "650(&/&3"5&
1 <?php 2 namespace App\Http; 3 4 use MyLovedFramework\Controller; 5
use MyLovedFramework\Request; 6 7 class BookController extends Controller 8 { 9 // ... 10 public function purchaseAction(Request $request) 11 { 12 if ($bookId = $request->query->get('bookId')) { 13 throw new \InvalidArgumentException(sprintf('The book %s cannot be found', $bookId)); 14 } 15 16 try { 17 $this->entityManager->beginTransaction(); 18 19 $book = $this->bookRepository->find($bookId); 20 21 $buyer = $this->currentSession()->user(); 22 $buyer->purchase($book); 23 24 $this->userRepository->persist($buyer); 25 26 $this->logger->log('info', sprintf('The book %s was purchased by user %s', $bookId, $buyer->getId())); 27 28 $this->mailer->send( 29 $buyer->getEmailAddress(), 30 'Your purchase is being processed', 31 'template_purchase.html' 32 ); 33 34 $this->entityManager->commit(); 35 36 return new Response(200, 'You purchase has been processed!'); 37 } catch (\Exception $e) { 38 $this->entityManager->rollback(); 39 $this->logger->log('error', $e->getMessage()); 40 return new Response(500, 'Problem to processing request'); 41 } 42 } 43 }
$0.1-&9*%"%& "-50"$01-".&/50 .*4563"%&$0/$&*504 %*'°$*-."/65&/*#*-*%"%&
$0/530--&3%3*7&/%&4*(/
5&45&4$0.1-&904&-&/504 10326&04'3".&803,4+¥/§03&40-7&&44&130#-&."
'3".&803,4 &/$"146-".&/50
<?php $content = file_get_contents('php://input'); $contentType = $_SERVER['CONTENT_TYPE']; if ($contentType ===
'application/json') { $data = json_decode($content, true); } else if ($contentType === 'application/xml') { $data = simplexml_load_string($requestContent); } // ... -08-&7&-"1*
<?php $data = $serializer->deserialize( $request->getContent(), $request->getContentType() ); &4$0/%&%&5"-)&4 &7*5"%61-*$*%"%&
-08-&7&-"1* $stmt = $db->prepare('SELECT * FROM Customer c WHERE c.age
>= ?'); $stmt->bindValue(1, 18); $stmt->execute(); $raw = $stmt->fetch(PDO::FETCH_ASSOC); $customer = Customer::fromArray($raw);
&4$0/%&04%&5"-)&4%&*.1-&.&/5"ª§0 $customer = $repository->createQueryBuilder('c') ->where('c.age >= :age') ->setParameter('age', 18) ->getQuery()
->getResult();
&4$0/%&04%&5"-)&4%&*.1-&.&/5"ª§0 $customers = $repository->olderAge();
0'0$0%&7&4&3/"4'6/$*0/"-*%"%&4 $03@*/&44%0."*/
&4$0-)&36.'3".&803, */45"-"36.130+&50&426&-&50 3&.07&3$0*4"4%&%&.0/453"ª§0 $3*"3&/5*%"%&4 "650(&/&3"5&
$3*"3$36%$0/530--&3`4 "650(&/&3"5&
1 <?php 2 namespace App\Http; 3 4 use MyLovedFramework\Controller; 5
use MyLovedFramework\Request; 6 7 class BookController extends Controller 8 { 9 // ... 10 public function purchaseAction(Request $request) 11 { 12 if ($bookId = $request->query->get('bookId')) { 13 throw new \InvalidArgumentException(sprintf('The book %s cannot be found', $bookId)); 14 } 15 16 try { 17 $this->entityManager->beginTransaction(); 18 19 $book = $this->bookRepository->find($bookId); 20 21 $buyer = $this->currentSession()->user(); 22 $buyer->purchase($book); 23 24 $this->userRepository->persist($buyer); 25 26 $this->logger->log('info', sprintf('The book %s was purchased by user %s', $bookId, $buyer->getId())); 27 28 $this->mailer->send( 29 $buyer->getEmailAddress(), 30 'Your purchase is being processed', 31 'template_purchase.html' 32 ); 33 34 $this->entityManager->commit(); 35 36 return new Response(200, 'You purchase has been processed!'); 37 } catch (\Exception $e) { 38 $this->entityManager->rollback(); 39 $this->logger->log('error', $e->getMessage()); 40 return new Response(500, 'Problem to processing request'); 41 } 42 } 43 }
1 <?php 2 namespace App\Http; 3 4 use MyLovedFramework\Controller; 5
use MyLovedFramework\Request; 6 7 class BookController extends Controller 8 { 9 // ... 10 public function purchaseAction(Request $request) 11 { 12 if ($bookId = $request->query->get('bookId')) { 13 throw new \InvalidArgumentException(sprintf('The book %s cannot be found', $bookId)); 14 } 15 16 try { 17 $this->entityManager->beginTransaction(); 18 19 $book = $this->bookRepository->find($bookId); 20 21 $buyer = $this->currentSession()->user(); 22 $buyer->purchase($book); 23 24 $this->userRepository->persist($buyer); 25 26 $this->logger->log('info', sprintf('The book %s was purchased by user %s', $bookId, $buyer->getId())); 27 28 $this->mailer->send( 29 $buyer->getEmailAddress(), 30 'Your purchase is being processed', 31 'template_purchase.html' 32 ); 33 34 $this->entityManager->commit(); 35 36 return new Response(200, 'You purchase has been processed!'); 37 } catch (\Exception $e) { 38 $this->entityManager->rollback(); 39 $this->logger->log('error', $e->getMessage()); 40 return new Response(500, 'Problem to processing request'); 41 } 42 } 43 } $¶%*(0$0.#"*9"$0&4§0&6. "-50"$01-".&/50"0'3".&803,
3&64"#*-*%"%&%&$¶%*(0¬*.1044°7&- 8&# 'VODJPOBMJEBEF $PNQSBSVN-JWSP $-* $PNNBOE-JOF
1 <?php 2 namespace App\Http; 3 4 use MyLovedFramework\Controller; 5
use MyLovedFramework\Request; 6 7 class BookController extends Controller 8 { 9 // ... 10 public function purchaseAction(Request $request) 11 { 12 if ($bookId = $request->query->get('bookId')) { 13 throw new \InvalidArgumentException(sprintf('The book %s cannot be found', $bookId)); 14 } 15 16 try { 17 $this->entityManager->beginTransaction(); 18 19 $book = $this->bookRepository->find($bookId); 20 21 $buyer = $this->currentSession()->user(); 22 $buyer->purchase($book); 23 24 $this->userRepository->persist($buyer); 25 26 $this->logger->log('info', sprintf('The book %s was purchased by user %s', $bookId, $buyer->getId())); 27 28 $this->mailer->send( 29 $buyer->getEmailAddress(), 30 'Your purchase is being processed', 31 'template_purchase.html' 32 ); 33 34 $this->entityManager->commit(); 35 36 return new Response(200, 'You purchase has been processed!'); 37 } catch (\Exception $e) { 38 $this->entityManager->rollback(); 39 $this->logger->log('error', $e->getMessage()); 40 return new Response(500, 'Problem to processing request'); 41 } 42 } #"*9"$0&4§0
4:.'0/: %0$53*/& 3&"$5+4 3"##*5.2 "11-*$"5*0/
&44&/$*"- %&5"-)&4 %&5"-)&4
5)&)&"350'40'58"3&*4*54"#*-*5:5040-7& %0."*/3&-"5&%130#-&.4'03*5464&3z &3*$&7"/4 %0."*/%3*7&/%&4*(/
"11 */'3"4536$563& 8&##3084&3 %"5"#"4& 5&3.*/"- '*-&4:45&. 06530 06530
$"."%"4
$0.6/*$"ª§0 A
.&/4"(&. .6/%0&95&3/0 "11 8&#)5551 305"4 .&/4"(&. "1*3&45'6- .&/4"(&. $-*
.&/4"(&. 8&#)5551 305"4 .&/4"(&. "1*3&45'6- .&/4"(&. $-*
Http Request Controller Form Repository Entity Value Object
l10354"/%"%"15&34z "326*5&563")&9"(0/"-
$0.."/%#64 $0.."/% )"/%-&3
."4"'*/"-026&¬6.$0.."/%
5*104%&.&/4"(&/4 *.1&3"5*7"4 $0.."/% */5&330("5*7" 26&3: */'03."5*7" &7&/5
5*104%&.&/4"(&/4 *.1&3"5*7"4 $0.."/% */5&330("5*7" 26&3: */'03."5*7" &7&/5
6.$0."/%0¬6."4*.1-&4.&/4"(&.*.1&3"5*7" 26&0#3*("503*".&/5&%&7&4&34*.1-&4
6.$0."/%0%&7&*/%*$"30$0.1035".&/5026& 6.646¥3*006$-*&/5&&41&3"%0"1-*$"5*70
3FHJTUFS6TFS 1VSDIBTF#PPL 4UBSU%JTDVTTJPO 4JHO6Q
%&7&$0/5&340.&/5&"4*/'03."ª¸&4 1"3"3&"-*;"346"5"3&'" <?php final class PurchaseBook { private $bookId; private
$readerId; public function __construct(BookId $bookId, ReaderId $readerId) { $this->bookId = $bookId; $this->readerId = $readerId; } }
*.65¥7&- <?php final class PurchaseBook { private $bookId; private $readerId;
public function __construct(BookId $bookId, ReaderId $readerId) { $this->bookId = $bookId; $this->readerId = $readerId; } public function bookId() : BookId { return $this->bookId; } public function readerId() : ReaderId { return $this->readerId; } }
4&3"6507"-*%"%" <?php final class PurchaseBook { private $bookId; private $readerId;
public function __construct(BookId $bookId, ReaderId $readerId) { Assertion::notNull($bookId); Assertion::notNull($readerId); $this->bookId = $bookId; $this->readerId = $readerId; } public function bookId() : BookId { return $this->bookId; } public function readerId() : ReaderId { return $this->readerId; } }
$0.."/%*44*.1-&"%50 %"5"53"/4'&30#+&$5 1 <?php 2 namespace App\Domain\Member; 3 4 class
RegisterMember 5 { 6 private $emailAddress; 7 private $password; 8 9 public function __construct(string $emailAddress, string $password) 10 { 11 $this->emailAddress = $emailAddress; 12 $this->password = $password; 13 } 14 15 public function emailAddress() : string 16 { 17 return $this->emailAddress; 18 } 19 20 public function password() : string 21 { 22 return $this->password; 23 } 24 }
$0.0*/5&313&5"304$0."/%04 $0.."/%)"/%-&3
<?php namespace App\Handler; class PuchaseBookHandler { public function __invoke(PurchaseCommand $command)
: void { try { $this->entityManager->beginTransaction(); $buyer = $this->userRepository->find($command->buyerId()); $book = $this->bookRepository->find($command->bookId()); $buyer->purchase($book); $this->userRepository->persist($buyer); $this->entityManager->commit(); $this->mailer->send( $buyer->getEmailAddress(), 'Your purchase is being processed', 'template_purchase.html' ); // Logs the purchase } catch (\Exception $e) { $this->entityManager->rollback(); // Logs when error is raised } } }
6.$0.."/%4¶10%&4&3"440$*"%0"6.)"/%-&3 6.)"/%-&34¶10%&4&3"440$*"%0"6.$0.."/%
6.)"/%-&3/§0%&7&3&503/"37"-03 4)06-%#&70*%
26"*4"43&"*47"/5"(&/4%&64"3&44""#03%"(&. 10%&4&3&9&$65"%0&.26"-26&31"35&%"46""1-*$"ª§0 8&#$-* /§05&3¥."*4$¶%*(03&1&5*%0 4&6$0/530--&3/§05&3¥3&(3"%&/&(¶$*0 ."*03'"$*-*%"%&1"3"5&45"3
<?php namespace Infrastructure\Command; class RegisterMemberConsoleCommand extends ConsoleCommand { public function
execute(Input $input) { $command = new RegisterMember( $input->getArgument('emailAddress'), $input->getArgument('password') ); $this->commandBus()->handle($command); } }
Http Request Controller Form INFRASTRUCTURE APPLICATION DOMAIN PurchaseCommand PurchaseHandler Entity
Repository
<?php namespace App\Http; use MyLovedFramework\Controller; use MyLovedFramework\Request; class BookController extends
Controller { // ... public function purchaseAction(Request $request) { $userId = $this->session->currentUser()->id(); $bookId = $request->get('bookId'); $command = new PurchaseBook($bookId, $userId); $this->commandBus->handle($command); // ... } }
¼/*$010/50%&&/53"%"1"3"46""1-*$"ª§0 4*/(-&10*/50''"*-63& 410'
"(03"70-5"/%01"3"0)"/%-&3 7".04"/"-*4"3$0.."*4$"65&-"
."4"/5&47".040-)"31"3"6."4*.1-&4 *.1-&.&/5"ª§0%&6.$0.."/%#64
<?php interface CommandBus { public function handle($command) : void; }
<?php class SimpleCommandBus implements CommandBus { private $handlerMap; public function
__construct($handlerMap = []) { $this->handlerMap = $handlerMap; } public function handle($command) : void { // .. $this->handlerMap(get_class($command))->handle($command); // .. } }
$handler = new PurchaseBookHandler(); $handlerMap = [ 'PurchaseCommand' => $handler
]; $command = new PurchaseCommand(); $commandBus = new SimpleCommandBus($handlerMap); $commandBus->handle($command);
<?php namespace App\Book\Application; use App\Book\Domain\PurchaseCommand; class PurchaseBookHandler { public function
__invoke(PurchaseCommand $command) { try { $this->entityManager->beginTransaction(); $buyer = $this->userRepository->find($command->buyerId()); $book = $this->bookRepository->find($command->bookId()); $buyer->purchase($book); $this->userRepository->persist($buyer); $this->entityManager->commit(); $this->mailer->send( $buyer->getEmailAddress(), 'Your purchase is being processed', 'template_purchase.html' ); $this->logger->log('success', 'Your purchase is being processed'); // Logs the purchase } catch (\Exception $e) { $this->entityManager->rollback(); $this->logger->log('error', sprintf('Error to purchase book %s.', $command->bookId())); } } }
."*4&04*/(-&3&410/4*#*-*5:13*/$*1-& 431
.JEEMFXBSF .JEEMFXBSF .JEEMFXBSF $PNNBOE)BOEMFS 3FRVFTU 3FTQPOTF
<?php class AnExampleMiddleware implements Middleware { public function execute($command, callable
$next) { // .. // .. $next($command); } }
<?php namespace League\Tactician\Doctrine\ORM; use Doctrine\ORM\EntityManagerInterface; use League\Tactician\Middleware; class TransactionMiddleware implements
Middleware { public function execute($command, callable $next) { $this->entityManager->beginTransaction(); try { $returnValue = $next($command); $this->entityManager->flush(); $this->entityManager->commit(); } catch (Exception $e) { $this->rollbackTransaction(); throw $e; } catch (Throwable $e) { $this->rollbackTransaction(); throw $e; } return $returnValue; } } $"40%&640 53"/4"ª¸&4$0. %0$53*/&03.
<?php namespace App\Book\Application; use App\Book\Domain\PurchaseCommand; class PurchaseBookHandler { public function
__invoke(PurchaseCommand $command) { try { $buyer = $this->userRepository->find($command->buyerId()); $book = $this->bookRepository->find($command->bookId()); $buyer->purchase($book); $this->userRepository->persist($buyer); $this->mailer->send( $buyer->getEmailAddress(), 'Your purchase is being processed', 'template_purchase.html' ); $this->logger->log('success', 'Your purchase is being processed’); } catch (\Exception $e) { $this->logger->log('error', sprintf('Error to purchase book %s.', $command->bookId())); } } }
<?php namespace League\Tactician\Logger; // .. class LoggerMiddleware implements Middleware {
// ... public function execute($command, callable $next) { $this->formatter->logCommandReceived($this->logger, $command); try { $returnValue = $next($command); } catch (Exception $e) { $this->formatter->logCommandFailed($this->logger, $command, $e); throw $e; } $this->formatter->logCommandSucceeded($this->logger, $command, $returnValue); return $returnValue; } } $"40%&640 -0(4
"(03"10%&.04&9&$65"3 /07"4"ª¸&4"/5&4&%&10*4 %"&9&$6ª§0%&6.$0."/%0
<?php namespace App\Book\Application; use App\Book\Domain\PurchaseCommand; class PurchaseBookHandler { public function
__invoke(PurchaseCommand $command) { try { $buyer = $this->userRepository->find($command->buyerId()); $book = $this->bookRepository->find($command->bookId()); $buyer->purchase($book); $this->userRepository->persist($buyer); $this->mailer->send( $buyer->getEmailAddress(), 'Your purchase is being processed', 'template_purchase.html' ); } catch (\Exception $e) { } } }
$0.640%&.*%%-&8"3&"41044*#*-*%"%&44§0*/¼.&3"4 -0((&3 7"-*%"503 4&$63*5: 1&3'03."/$&.&53*$4 46"/&$&44*%"%&
IUUQTHJUIVCDPN4JNQMF#VT.FTTBHF#VT
IUUQTHJUIVCDPNUIFQIQMFBHVFUBDUJDJBO
7".040-)"30"/5&4&%&10*4
<?php namespace App\Book\Application; use App\Book\Domain\PurchaseCommand; class PurchaseBookHandler { public function
__invoke(PurchaseCommand $command) : void { $buyer = $this->userRepository->find($command->buyerId()); $book = $this->bookRepository->find($command->bookId()); $buyer->purchase($book); $this->userRepository->persist($buyer); $this->mailer->send( $buyer->getEmailAddress(), 'Your purchase is being processed', 'template_purchase.html' ); } } <?php namespace App\Http; use MyLovedFramework\Controller; use MyLovedFramework\Request; class BookController extends Controller { // ... public function purchaseAction(Request $request) { $userId = $this->session->currentUser()->id(); $bookId = $request->get('bookId'); $command = new PurchaseBook($bookId, $userId); $this->commandHandler->handle($command); // ... } }
None
/04*-7&3#6--&5 IUUQXXXJUVELQFPQMFIFTK#461BSUJLMFSOPTJMWFSCVMMJUQEG
IUUQDVMUUUDPNDSFBUJOHVTJOHDPNNBOECVT IUUQTQIQBOETZNGPOZNBUUIJBTOPCBDLOMBXBWFPGDPNNBOECVTFT IUUQTQIQBOETZNGPOZNBUUIJBTOPCBDLOMSFTQPOTJCJMJUJFTPGUIFDPNNBOECVT IUUQTIBXONDDPPMDPNNBOECVT IUUQTHOVHBUHJUIVCJPUPXBSETDRSTDPNNBOECVTIUNM 3FGFSÊODJBT
0#3*("%0