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
RDBMS
Legacy System
Session
Storage
Collect & Export
K-V
Store
Webserver
Key-Value
Store
Search
FURY Frontend FURY Backend
Legacy
RDBMS
Legacy System
FURY Requests
200OK
Session
Storage
K-V
Store
Webserver
Key-Value
Store
Search
FURY Frontend FURY Backend
Legacy
RDBMS
Legacy System
Requests to Legacy System
404NOTFOUND
Session
Storage
K-V
Store
Webserver
Key-Value
Store
Search
FURY Frontend FURY Backend
Legacy
RDBMS
Legacy System
Requests to Legacy System
404NOTFOUND
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 KartenmachereiIPC2016;
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
Basket

Service
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/
jobs@kartenmacherei.de
http://www.kartenmacherei.de/recruiting
https://tech.kartenmacherei.de/
@techdotkam

Don't fear the Walking Dead @ IPC 2016