Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Microservice within a Monolith #devdays2019
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Joop Lammerts
May 15, 2019
Programming
93
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
60
How to improve your team synergy w/The Attitude Model #DPC19
jlammerts
0
220
Microservice within a Monolith #phpday
jlammerts
0
190
Microservice within a Monolith v1
jlammerts
0
160
The Attitude Model
jlammerts
0
46
Improve your team synergy w/The Attitude model
jlammerts
0
400
Other Decks in Programming
See All in Programming
RAG の “R” を Swift で覗いてみる 〜「意味から探す」検索の仕組み〜
nao_randd
0
160
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Kaigi on Rails 2026, Tokyo, Japan
marcoroth
2
220
3回つくってわかった、 定期実行ジョブ基盤の本質情報
takai
0
460
設計次第でAIコードの読む量は減らせる / designing-for-code-reading
minodriven
35
17k
Web Framework Benchmark Battle 2026, Any%
a_matsuda
2
2k
月間100万人のサービス刷新で、 技術選定をHonoに振り切った話
yutobara
8
6.6k
機械に任せるテスト、人が見るテスト ⽣成AIで引き直した、運⽤保守フェーズの線引き
dske104
0
190
AI主導のプログラミング学習アプリを作ってみた
econanringo
0
180
AIで開発が速くなった先で、開発チームは何を目指すか
shihochan
0
920
作って学ぶSpring BootのAuto Configuration / JJUG Night Seminar 2026-10-19
rshindo
0
140
Long Living Tokens, Die, Die, Die! (or: The One Ring is Banned in Modern)
sylph01
0
300
golang-bob.pdf
iwatsukayura
0
130
Featured
See All Featured
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
480
RailsConf 2023
tenderlove
30
1.6k
We Are The Robots
honzajavorek
0
400
It's Worth the Effort
3n
188
29k
What the history of the web can teach us about the future of AI
inesmontani
PRO
2
740
New Earth Scene 8
popppiees
4
2.7k
The Pragmatic Product Professional
lauravandoore
37
7.5k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
300
How GitHub (no longer) Works
holman
316
150k
Tell your own story through comics
letsgokoyo
1
1.1k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
460
The Art of Programming - Codeland 2020
erikaheidi
57
14k
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