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
280
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
54
Background Jobs - Ao infinito e além
hugohenrique
1
150
Other Decks in Programming
See All in Programming
【KMC春合宿2024】実装視点で見るNeural Radiance Fields
runningoutrate
0
150
チームでモデリングを育てるうえで 考えたこと・気づいたこと / Cultivating Modeling in Teams: Thoughts and Insights
mackey0225
5
2.4k
App Router への移行は「改善」となり得るのか?/ Can migration to App Router be an improvement
takefumiyoshii
1
140
品質とスピードを両立: TypeScriptの柔軟な型システムをバックエンドで活用する
kosui
5
1.1k
今の SmartHR にエンジニアで入社するとどうなるの?
daisukeshinoku
3
1.4k
CircleCIを活用して AWSへの継続的デリバリーを 実践する
coconala_engineer
1
110
AppDeveloperCon 2024 EU: Building polyglot developer experiences in 2024
salaboy
0
380
Crafting a Own PHP - ウキウキ手作りミニマリストPHP
uzulla
4
1.1k
Laravel OpenAPIによる"辛くない"スキーマ駆動開発
kentaroutakeda
2
2.1k
デザインシステムで Tailwind CSSとCSS in JSに分散投資をしたら良かった話
fsubal
14
3.4k
object-oriented-conference-2024
fuwasegu
6
1.9k
C# 大統一理論推進委員会 会員のための Unity Package Manager プロジェクト構成案
monry
PRO
0
580
Featured
See All Featured
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
242
20k
From Idea to $5000 a Month in 5 Months
shpigford
376
45k
How STYLIGHT went responsive
nonsquared
92
4.7k
Imperfection Machines: The Place of Print at Facebook
scottboms
257
12k
Embracing the Ebb and Flow
colly
78
4.1k
Bootstrapping a Software Product
garrettdimon
PRO
302
110k
ParisWeb 2013: Learning to Love: Crash Course in Emotional UX Design
dotmariusz
101
6.6k
Navigating Team Friction
lara
177
13k
Six Lessons from altMBA
skipperchong
19
2.9k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
124
32k
Reflections from 52 weeks, 52 projects
jeffersonlam
343
19k
A Philosophy of Restraint
colly
195
15k
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