don't fear the walking dead @ ipc 2016
TRANSCRIPT
HOW WE GET RID OF OUR MONOLITH
DON’T
How we started• built on top of a standard e-commerce platform
• a lot of custom code to support a wide combination of product options
Feature X Feature Y Feature Z
Core Code Customization
The challenges of a growing business
• serious performance issues
• own developers don’t feel comfortable with the code
• adding individual features is too complex
• upgrading is hard
Finding a way out
You don’t have to replace everything at once!
What part of the software causes the highest amount of pain?
• category pages
• product detail pages
Planning
Traditional Architecture
PHP Application
Session Storage
RDBMS
Webserver
PIM / ERP
Incoming Request
PHP Application
Session Storage
RDBMS
Webserver
PIM / ERP
Caching
PHP Application
Session Storage
RDBMS
Webserver
PIM / ERP
Cache
Caching
PHP Application
Session Storage
RDBMS
Webserver
PIM / ERP
Cache
Our Initial Setup
Legacy System
Session Storage
RDBMS
Webserver
PIM / ERP
Cache
Idea
Legacy System
Session Storage
RDBMS
Webserver New Software
FURY Frontend Key-Value Store
getCategoryPage('hochzeit');
"<html><head>…</head><body>…</body></html>"
FURY Frontend
Key-Value Store
["SBU06HE"
,"TEOD3HE"
,"ANW04HD"
]
Search
search("{'
category':
'hochzeit'
, 'filters
':{}}");
getTiles(["SBU06HE","TEOD3HE","ANW04HD"]);
["<div class="category-item">…</div>", …]
CQRS-based Architecture
PHP Frontend
Session Storage
Webserver Key/Value Store PHP Backend
PIM / ERPSearch Engine
Incoming Request
PHP Frontend
Session Storage
Webserver Key/Value Store PHP Backend
PIM / ERPSearch Engine
New Data
PHP Frontend
Session Storage
Webserver Key/Value Store PHP Backend
PIM / ERPSearch Engine
Webserver
Key-Value Store
Search
FURY Frontend FURY Backend
FURY Components
Webserver
Key-Value Store
Search
FURY Frontend FURY Backend
Legacy RDBMSLegacy System Session
Storage
Collect & Export
K-V Store
Webserver
Key-Value Store
Search
FURY Frontend FURY Backend
Legacy RDBMSLegacy System
FURY Requests
200 OK
Session Storage
K-V Store
Webserver
Key-Value Store
Search
FURY Frontend FURY Backend
Legacy RDBMSLegacy System
Requests to Legacy System
404 NOT FOUND
Session Storage
K-V Store
Webserver
Key-Value Store
Search
FURY Frontend FURY Backend
Legacy RDBMSLegacy System
Requests to Legacy System
404 NOT FOUND
200 OK
Session Storage
K-V Store
Learnings• we have full responsibility now
• no framework needed
• no relational database needed
• accessing the legacy session storage was too slow
• solved by adding a read slave for FURY
Goods and Bads• three months of development until first launch
• fully object-oriented
• easy refactoring thanks to 100% code coverage
• not enough automated acceptance tests
• dependencies to legacy software (database changes, API calls)
A/B Deployment
Webserver (Router)
Webserver
FURY Frontend
Server A
K/V StoreSearch
FURY Backend
Webserver
FURY Frontend
Server B
K/V StoreSearch
FURY Backend
Webserver (Router)
Webserver
FURY Frontend
Server A
K/V StoreSearch
FURY Backend
Webserver
FURY Frontend
Server B
K/V StoreSearch
FURY Backend
active = A
Webserver (Router)
Webserver
FURY Frontend
Server A
K/V StoreSearch
FURY Backend
Webserver
FURY Frontend
Server B
K/V StoreSearch
FURY Backend
active = B
Webserver (Router)
Webserver
FURY Frontend
Server A
K/V StoreSearch
FURY Backend
active = B
RDBMS Read Slave
Webserver
FURY Frontend
Server A
K/V StoreSearch
FURY Backend
Webserver (Router)
Build Server
Deploy Code
active = B
RDBMS Read Slave
Webserver
FURY Frontend
Server A
K/V StoreSearch
FURY Backend
Webserver (Router)
Build Server
Deploy Code
Collect & Export
active = B
RDBMS Read Slave
Webserver
FURY Frontend
Server A
K/V StoreSearch
FURY Backend
Webserver (Router)
Build Server
Deploy Code
Collect & Export
Smoke Tests
active = B
Smoke Tests<?php namespace Kartenmacherei\IPC2016;
use PHPUnit_Framework_TestCase;
class SmokeTest extends PHPUnit_Framework_TestCase { /** * @dataProvider furyUrlProvider * * @param Url $url */ public function testFuryUrl(Url $url) { $result = $this->sendGetRequest($url);
$this->assertSame(200, $result->getStatusCode()); $this->assertNotEmpty($result->getBody()); $this->assertLessThanOrEqual(100, $result->getTimeToFirstByteInMilliseconds()); }
RDBMS Read Slave
Webserver
FURY Frontend
Server A
K/V StoreSearch
FURY Backend
Webserver (Router)
Build Server
Deploy Code
Collect & Export
Smoke Tests
active = B
RDBMS Read Slave
Webserver
FURY Frontend
Server A
K/V StoreSearch
FURY Backend
Webserver (Router)
Build Server
Deploy Code
Collect & Export
Smoke Tests
active = B
RDBMS Read Slave
Webserver
FURY Frontend
Server A
K/V StoreSearch
FURY Backend
Webserver (Router)
Build Server
Deploy Code
Collect & Export
Smoke Tests
Switch to A
active = A
RDBMS Read Slave
Webserver
FURY Frontend
Server A
K/V StoreSearch
FURY Backend
active = B Webserver (Router)
Build Server
Deploy Code
Collect & Export
What's next?
Client
/product
Client
/checkout/cart
Client
/checkout/basket
Basket Service
Client
/checkout/basket
BasketService
Next steps• open-source our CQRS RESTful Framework
• replace the next parts of the legacy system with new, self-contained components / services
Next steps• open-source our CQRS RESTful Framework
• improve our deployment process
• replace the next parts of the legacy system with new, self-contained components / services
Next steps• open-source our CQRS RESTful Framework
• improve our deployment process
• replace the next parts of the legacy system with new, self-contained components / services
"one hundred fifty-seven quinvigintillion, seven hundred eighty-six quattuorvigintillion, six hundred fifty-seven trevigintillion, seven hundred
forty-seven duovigintillion, three hundred forty unvigintillion, one hundred eighty-six vigintillion, (…) nine hundred forty-five quintillion, eight hundred
twenty-eight quadrillion, two hundred seventy trillion, eighty billion, …"
Q&A
https://www.facebook.com/kartenmacherei/
http://www.kartenmacherei.de/recruiting
https://tech.kartenmacherei.de/
@techdotkam