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
Microservice within a Monolith #phpday
Search
Joop Lammerts
May 11, 2019
Programming
180
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Microservice within a Monolith #phpday
Joop Lammerts
May 11, 2019
More Decks by Joop Lammerts
See All by Joop Lammerts
_Rootnet__You_re_Agile_is_broken__and_here_is_how_to_fix_it.pdf
jlammerts
0
60
How to improve your team synergy w/The Attitude Model #DPC19
jlammerts
0
210
Microservice within a Monolith #devdays2019
jlammerts
0
92
Microservice within a Monolith v1
jlammerts
0
150
The Attitude Model
jlammerts
0
41
Improve your team synergy w/The Attitude model
jlammerts
0
390
Other Decks in Programming
See All in Programming
才能?センス?知らん、 続けたもん勝ちだ。-- 結婚・出産・癌を越えてなお、私がプロダクトを創り続ける理由
16bitidol
2
910
PHP初心者セッション2026 〜生成AIでは見えない裏側を知る:今だからLAMPを通して仕組みを学ぶ〜
kashioka
0
660
【やさしく解説 設計編・中級 #1】一つの車に、運転手は一人 ~ある倉庫システムの事例から~
panda728
PRO
0
190
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
240
PHP Application における Kubernetes 内 gRPC 通信
ganchiku
0
530
AI時代の仕事技芸論〜ソフトウェア開発で「遊ぶように働く」職人的熟達のすすめ(スクフェス仙台 2026バージョン)
kuranuki
0
710
生成AI導入の「期待外れ」を乗り越える ー 開発フロー改革が目指す、真の組織変革
starfish719
0
2.1k
自作OSでスライド発表する
uyuki234
1
3.9k
【やさしく解説 設計編・中級 #4】ルールの寿命と、システムの年輪
panda728
PRO
2
170
Lean は証明の正しさを確認するためだけのツールって思ってませんか?
inoueasei
1
110
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
190
『コードを書く以外の』エンジニアリング〜課金基盤移行プロジェクト推進のためのTips4選
yuriko1211
0
540
Featured
See All Featured
Large-scale JavaScript Application Architecture
addyosmani
515
110k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
190
Optimizing for Happiness
mojombo
378
71k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Odyssey Design
rkendrick25
PRO
2
730
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
230
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
490
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
380
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.7k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
The Curious Case for Waylosing
cassininazir
1
440
Transcript
Microservices within a Monolith #phpday @jlammerts
Joop Lammerts Developer @procurios for +3 years @jlammerts
Procurios Cluster
Procurios Cluster for context
Our Monolith
Our monolith ~ backend: 3.000.000 lines of code distributed over
18.000 PHP files ~ frontend: 350.000 lines in 1800 JavaScript files 800.000 lines of CSS code
Usage
Usage • 2000 clients • 800.000 users • 500.000 visitors
an hour
None
Monolith
None
Microservices
Service #1 Service #2 Service #3
Microservices, or microservice architecture, is an approach to application development
in which a large application is built as a suite of modular components or services. Assumption
Microservices, or microservice architecture, is an approach to application development
in which a large application is built as a suite of modular components or services. Assumption
Modules
Modulair Monolith
Modulair Monolith • Bounded context with no dependencies on each
other • Information can be duplicated for each bounded context
None
What to do with your legacy Monolith?
Project |--- core |--- modules |--- cms |--- meeting |---
relation |--- user
Project |--- core |--- modules |--- cms |--- meeting ->
attendee -> meeting -> registration -> ticket |--- relation |--- user
final class RegistrationController { public function register() { $userId =
$_SESSION['userId']; $pdo = new \PDO('localhost'); $statement = $pdo->prepare(" SELECT * FROM `user` WHERE `id` = ? "); $statement->execute([$userId]); $userData = $statement->fetch()[0]; if (!$userData) { HttpResponse::redirect('/login'); } $form = $this->getRegistrationForm(); $data = $form->getData(); if (!$data['meetingId'] || !$data['ticketId'] || !$data['remark']) { return $form; } $statement = $pdo->prepare(" UPDATE `tickets` SET `sold` = 1 WHERE WHERE `id` = ? "); $statement->execute([ $data['ticketId'] ]); if ($statement->rowCount() !== 1) { return 'There are no tickets available'; } $statement = $pdo->prepare(" INSERT INTO `attendee` SET `user_id` = ?, `first_name` = ?, `last_name` = ?, `meeting_id` = ?, `ticket_id` = ?, `remark` = ?, "); $statement->execute([ $userData['id'], $userData['firstName'], $userData['name'], $data['meetingId'], $data['ticketId'], $data['remark'], ]); /* * send confirmation stuff */ \HttpResponse::redirect('/'); } }
namespace Meeting\Registration; final class RegistrationController { public function register() {
$userId = $_SESSION['userId']; $pdo = new \PDO('localhost'); $statement = $pdo->prepare(" SELECT * FROM `user` WHERE `id` = ? "); $statement->execute([$userId]); $userData = $statement->fetch()[0]; if (!$userData) { HttpResponse::redirect('/login'); } // }
namespace Meeting\Registration; final class RegistrationController { public function register() {
$user = UserService::getCurrentUser(); if (!$user->isAuthenticated()) { HttpResponse::redirect('/login'); } // }
namespace User; final class UserService { public static function getCurrentUser():
User { $userId = $_SESSION['userId'] ?? null; if ($userId === null) { return User::guest(); } $pdo = new \PDO('localhost'); $statement = $pdo->prepare(" SELECT * FROM `user` WHERE `id` = ? "); $statement->execute([$userId]); $userData = $statement->fetch()[0]; return $userData ? User::populate($userData); : User::guest(); }
User is now a bounded context
final class RegistrationController { public function register() { $user =
UserService::getCurrentUser(); if (!$user->isAuthenticated()) { HttpResponse::redirect('/login'); } $form = $this->getRegistrationForm(); $data = $form->getData(); if (!$data['meetingId'] || !$data['ticketId'] || !$data['remark']) { return $form; } $statement = $pdo->prepare(" UPDATE `tickets` SET `sold` = 1 WHERE WHERE `id` = ? "); $statement->execute([ $data['ticketId'] ]); if ($statement->rowCount() !== 1) { return 'There are no tickets available'; } $statement = $pdo->prepare(" INSERT INTO `attendee` SET `user_id` = ?, `first_name` = ?, `last_name` = ?, `meeting_id` = ?, `ticket_id` = ?, `remark` = ?, "); $statement->execute([ $userData['id'], $userData['firstName'], $userData['name'], $data['meetingId'], $data['ticketId'], $data['remark'], ]); /* * send confirmation stuff */ \HttpResponse::redirect('/'); } }
But wait, there is more!
public function register() { // $form = $this->getRegistrationForm(); $data =
$form->getData(); if (!$data['meetingId'] || !$data['ticketId'] || !$data['remark']) { return $form; } $statement = $pdo->prepare(" UPDATE `tickets` SET `sold` = 1 WHERE `id` = ? AND `sold` = 0 "); $statement->execute([ $data['ticketId'] ]); if ($statement->rowCount() !== 1) { return 'There are no tickets available'; } // }
public function register() { // $form = $this->getRegistrationForm(); $data =
$form->getData(); if (!$data['meetingId'] || !$data['ticketId'] || !$data['remark']) { return $form; } try { $ticket = TicketService::purchase($data['ticketId']); } catch (CouldNotPurchaseTicket $e) { return 'There are no tickets available'; } // }
final class TicketService { public static function purchase(int $ticketId): Ticket
{ $connection = DB::getConnection(); $statement = $connection->prepare(" UPDATE `tickets` SET `sold` = 1 WHERE `id` = ? AND `sold` = 0 "); $statement->execute([ $data['ticketId'] ]); if ($statement->rowCount() !== 1) { throw CouldNotPurchaseTicket::becauseNoTicketsLef(); } return new Ticket($ticketId); } }
public function register() { // $statement = $pdo->prepare(" INSERT INTO
`attendee` SET `user_id` = ?, `first_name` = ?, `last_name` = ?, `meeting_id` = ?, `ticket_id` = ?, `remark` = ?, "); $statement->execute([ $user->getId(), $user->getFirstName(), $user->getName(), $data['meetingId'], $data['ticketId'], $data['remark'], ]); // }
public function register() { // $attendee = new Attendee( $user->getId(),
$user->getFirstName(), $user->getName() ); RegistrationService::register($data['meetingId'], $attendee, $ticket, $data['remark']); /* * send confirmation stuff */ HttpResponse::redirect('/'); }
Modules sent messages
final class RegistrationService { public static function register(int $meetingId, Attendee
$attendee, Ticket $ticket, string $remark): void { $repository = self::getRepository(); $registration = $repository->create($meetingId, $attendee, $ticket, $remark); $registrationCreated = new RegistrationCreated($registration); foreach (self::getListenerProvider()->getListenersForEvent($registrationCreated) as $listener) { $listener->handle($registrationCreated); } } }
final class NotifyAttendee { public function handle(RegistrationCreated $registrationCreated): void {
// send confirmation stuff } }
final class RegistrationController { public function register() { $user =
UserService::getCurrentUser(); if (!$user->isAuthenticated()) { HttpResponse::redirect('/login'); } $form = $this->getRegistrationForm(); $data = $form->getData(); if (!$data['meetingId'] || !$data['ticketId'] || !$data['remark']) { return $form; } try { $ticket = TicketService::purchase($data['ticketId']); } catch (CouldNotPurchaseTicket $e) { return 'There are no tickets available'; } $attendee = new Attendee($user->getId(), $user->getFirstName(), $user->getName()); RegistrationService::register($data['meetingId'], $attendee, $ticket, $data['remark']); HttpResponse::redirect('/'); } }
Take aways Locate and isolate Bounded contexts and turn them
into modules Let the world know what changed in an Event Driven plugin architecture
Joop Lammerts Website: www.procurios.com Twitter: @jlammerts Feedback: https://joind.in/talk/7c6ed