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 #devdays2019
Search
Joop Lammerts
May 15, 2019
Programming
92
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Microservice within a Monolith #devdays2019
Joop Lammerts
May 15, 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
59
How to improve your team synergy w/The Attitude Model #DPC19
jlammerts
0
200
Microservice within a Monolith #phpday
jlammerts
0
180
Microservice within a Monolith v1
jlammerts
0
150
The Attitude Model
jlammerts
0
40
Improve your team synergy w/The Attitude model
jlammerts
0
390
Other Decks in Programming
See All in Programming
「AIで開発し、AIを届ける」をEvalでつなぐ 〜AIネイティブに始めるプロダクト開発の実践〜 / Connecting "Develop with AI, deliver AI" with Eval
rkaga
4
5.5k
SREは、MCPとSRE Agentをこう使え!
kazumax55
0
130
Observability in Practice:Grafana 與 Edge Device SRE 的那些事
blueswen
0
180
さぁV100、メモリをお食べ・・・
nilpe
0
160
Honoでのサプライチェーン侵害対策 〜 3つのライブラリに学ぶ
yusukebe
7
1.5k
[2026年度第1回ORセミナー] 計画最適化ベンチャーと競技プログラミング人材
terryu16
0
280
生成AI時代にこそ効くGo | Why Go Works in the Age of Generative AI
mom0tomo
8
3.4k
セキュリティの専門家じゃなくてもできる。「セキュリティ意識」をアップデートして サプライチェーン攻撃への耐性を高めよう。
tk3fftk
5
990
Datadog LLM Observabilityで実現する 安全なLLM Usage 管理
3150
0
130
AIキャラアプリkaiwaの低遅延音声通話基盤をどう作ったか - AWS Gravitonで支える低遅延・低コストAI Agent基盤
mogamit
0
130
TAKTでAI駆動開発の品質を設計する
j5ik2o
7
1.6k
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2.4k
Featured
See All Featured
4 Signs Your Business is Dying
shpigford
187
22k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
640
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
2
270
Building AI with AI
inesmontani
PRO
1
1.1k
Into the Great Unknown - MozCon
thekraken
41
2.6k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
67
55k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
380
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
190
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
350
The Language of Interfaces
destraynor
162
27k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
260
Transcript
Microservices within a Monolith Join at slido.com #devdays2019 @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
Modular Monolith
Modular 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(" /* */ "); $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(" /* */ "); $statement->execute([ $data['ticketId'] ]); if ($statement->rowCount() !== 1) { throw CouldNotPurchaseTicket::becauseNoTicketsLef(); } return new Ticket($ticketId); } }
public function register() { // $statement = $pdo->prepare(" /* */
"); $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()); $registration = new Registration($data['meetingId'], $attendee, $ticket, $data['remark']); RegistrationService::register($registration); /* * send confirmation stuff */ HttpResponse::redirect('/'); }
Modules sent messages
final class RegistrationService { public static function register(Regitstation $registration): void
{ $repository = self::getRepository(); $repository->save($registration); $registrationCreated = new RegistrationCreated($registration); foreach (self::getListenerProvider()->getListenersForEvent($registrationCreated) as $listener) { $listener->handle($registrationCreated); } } }
final class NotifyAttendee { public function handle(RegistrationCreated $registrationCreated): void {
$registration = $registrationCreated->getRegistration(); mail( $registration->getEmailAddressAsString(), ‘See you soon’, sprintf( ‘Hi %s<br /> See you soon at %s ‘, $registration->getFirstName(), $registration->getMeetingName() ); } }
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()); $registration = new Registration($data['meetingId'], $attendee, $ticket, $data['remark']); RegistrationService::register($registration); HttpResponse::redirect('/'); } }
But wait, there is more!
final class NotifyAttendee { public function handle(RegistrationCreated $registrationCreated): void {
$registration = $registrationCreated->getRegistration(); mail( $registration->getEmailAddressAsString(), ‘See you soon’, sprintf( ‘Hi %s<br /> See you soon at %s ‘, $registration->getFirstName(), $registration->getMeetingName() ); } }
final class NotifyAttendeeByMandrill { public function handle(RegistrationCreated $registrationCreated): void {
$registration = $registrationCreated->getRegistration(); $mandrill new Mandrill('/* */'); $mandrillMessage = [ /* */ ]; try { $mandrill->messages->send($mandrillMessage); } catch(Mandrill_Error $e) { // } }
Oh no! The service is down
final class NotifyAttendeeJob { public function __construct(AsyncMessageBus $bus) { $this->bus
= $bus; } public function handle(RegistrationCreated $registrationCreated): void { $registration = $registrationCreated->getRegistration(); $message new SendConfimationMessage($registration); $this->bus->send($message); }
final class SendConfimationMessageWorker { public function handle(SendConfimationMessage $message): void {
$mandrill new Mandrill('/* */'); $mandrillMessage = [ /* */ ]; try { $mandrill->messages->send($mandrillMessage); $mandrillMessage->markSend(); } catch(Mandrill_Error $e) { // } }
Takeaways 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