SlideShare a Scribd company logo
Max Voloshin - "Organization of frontend development for products with microservices"
Max Voloshin
● Location: Dnipro, Ukraine
● Position: Team Lead, OWOX
● In professional software development since 2010
● Fan of microservices and continuous deployment
Photographer: Avilov Alexandr
OWOX Business Intelligence
As business:
● More than 1 million transactions per week in our clients' projects
● 1 000+ projects in 50 countries rely on our solutions
● $ 200 000+ daily handled our clients' costs on advertising
As software:
● 10+ microservices in PHP/Java
● Web Components (Polymer 1.8)
● Several releases every day
Why am I here?
I want to share 9 improvements
for making frontend development easier
Who cares?
Frontend development
may be a constraint for your product
Let’s improve!
#1
Let frontend developer
don't deal with content management
#1
<div class="e-wrap">
<div class="e-grid-col1 e-grid-col-empty"></div>
<div class="e-grid-col8" role="main">
<h2 class="e-page-title">
Page not found
</h2>
<p class="error-message">
Incorrectly typed address or a page on the website no longer exists
</p>
<p class="error-link-wrap"><a href="/" class="error-link">
Home
</a></p>
</div>
<div class="clear"></div>
</div>
Do you really want
to change one selling text to another?..
#1
Use mnemonic names of texts
instead of hard-coded content
#1
Admin panel
for content
Write contentContent
manager
Frontend
developer Frontend
Write code
Matched by mnemonic name
#1
Polymer
<div class="e-wrap">
<div class="e-grid-col1 e-grid-col-empty"></div>
<div class="e-grid-col8" role="main">
<h2 class="e-page-title">
<html-text name="NotFoundPage.Title"></html-text>
</h2>
<p class="error-message">
<html-text name="NotFoundPage.Message"></html-text>
</p>
<p class="error-link-wrap"><a href="/" class="error-link">
<html-text name="HomePage.Title"></html-text>
</a></p>
</div>
</div>
Bonus: easy internalization
#1
#2
Let frontend developer
use HTML/JS templating
#2
.answers-i-link:before { CSS as Smarty (PHP) template
content: "";
position: absolute;
left: -30px;
width: 25px;
height: 25px;
background: url('/*{$settings.path}*//icons.png') 0 -414px no-repeat;
}
.answers-i-link:hover:before {
background: url('/*{$settings.path}*//icons.png') 0 -373px no-repeat;
}
.answers-i-link.active:before {
background: url('/*{$settings.path}*//icons.png') 0 -700px no-repeat;
}
.answers-i-link.active:hover:before {
background: url('/*{$settings.path}*//icons.png') 0 -659px no-repeat;
}
{foreach $tree as $section} HTML as Smarty (PHP) template
<h3 class="level-{$level}">{$section['title']}</h3>
{if $section['children']}
{include file='faq/subjects.tpl' tree=$section['children'] level=$level+1}
{/if}
{if isset($section['records'])}
{foreach $section['records'] as $record}
{if $is_contents}
<a href="#record-{$record.id}"
class="level-{$level}">{$record.title}</a><br />
{else}
<a name="record-{$record.id}"
class="level-{$level}">{$record.title}</a><br />
{$record['answer'] nofilter}
{/if}
{/foreach}
{/if}
{/foreach}
HTML as Smarty (PHP) template
<div class="b-expenses-head">
{foreach from=$manual_costs item="month" name="tabs"}
<div id="manual-costs-tab-{$smarty.foreach.tabs.index}" class="inline
b-expenses-head-month {if $month['is_active']}active{/if}">
<span class="b-expenses-month-title">{$month['month_title']}</span>
</div>
<script type="text/javascript">
ManualCosts.addGroup(new
ManualCostsTab_class({$smarty.foreach.tabs.index}, '{$month['hash']}'));
</script>
{/foreach}
</div>
JS as Smarty (PHP) template
initialize: function(data, connect_service_access) {
this.parent(data, connect_service_access);
Object.append(
this._templates,
{access_create: '/*{template_script_fetch file="access_create.jst" }*/'}
);
this._popup.addEvent('createAccess', this.onCreateAccess.bind(this));
},
onCreateAccess: function() {
this.send(
'/*{$menu.my.href}*/users/access/simple_services#createAccess',
{
service_name: this._data['service_name'],
token: token,
service_account: service_account
}
);
}
Do you really want to learn and debug
non JS/HTML templating?..
#2
Use HTML/JS templating,
use data as JSON
#2
Polymer
<template is="dom-if" if="[[!isEmpty(url)]]">
<page-item item-action>
<a is="pushstate-anchor" href="[[url]]" class="item-action">
<text-html name="Page.Item.Actions.Edit"></text-html>
</a>
</page-item>
</template>
Polymer
<dom-module id="page-external-link-styles">
<template>
<style include="shared-styles">
:host {
display: inline;
white-space: nowrap;
}
:host(:not(.no-link-style)) a {
color: var(--blue-color-main);
text-decoration: none;
}
</style>
</template>
</dom-module>
Bonus: static code analysis & tests
#2
#3
Let frontend developer
don't deal with backend routing
#3
Symfony (PHP) routing
# app/config/routing.yml
blog_list:
path: /blog/{page}
defaults: { _controller: AppBundle:Blog:list, page: 1 }
requirements:
page: 'd+'
blog_show:
# ...
Do you really want to learn and debug
backend routing?..
#3
Use frontend routing
via Single Page Application
#3
Polymer
<app-router id="appRouter" mode="pushstate" init="manual">
<app-route
id="dataPipeline"
path$="[[globals.pathPrefix]]/:context/pipeline/"
element="pipeline-page"
import="/components/pages/pipeline/pipeline-page.html"
></app-route>
<app-route
id="costDataPage"
path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/"
redirect="history/"
></app-route>
<app-route
id="costDataHistoryPage"
path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/history/"
element="cost-data-page"
import="/components/pages/cost-data/cost-data-page.html"
></app-route>
</app-router>
#4
Let frontend developer
don't deal with stagnated framework
#4
Switching framework is hard
● You can’t pause product development
● You can't release all changes at one time
#4
Two approaches
for framework switching
#4
1. Continuous deploy
then single release
#4
UI v1 (MooTools) → UI v2 (Polymer 0.5)
t
Release v1
Deploy v2
(part #1)
Change v1
prod
Deploy v2
(part #2)
Release v2
Deploy v2
(part #N)
...
Deploy != Release
#4
UI v1: https://domain.com/
UI v2: https://domain.com/v2/ ← SPA root
Continuous deploy then single release
+ Compatible with design switching
- Suitable only for small applications because of effort
duplication
#4
Off topic: why Polymer?
● Material Design out of the box
● Possibilities to UI reuse (not only JS)
● Google promotion of Web Components
● #UseThePlatform
#4
2. Page-by-page release
#4
The first law of holes
#4
Limitations:
1. Only one version of Polymer can be on page
2. We have Single Page Application
Solution: “break points” in Single Page Application
UI v2 — domain.com/v2/ UI v3 — domain.com/v3/
/foo/
/bar/
/baz/
/qux/
/foo/
/bar/
/baz/
/qux/
UI v2 (Polymer 0.5) → UI v3 (Polymer 1.8)
#4
UI v2
<app-router id="router" mode="pushstate" init="manual"
<app-route
id="pipeline"
path="/ui/:context/pipeline/"
import="{{$.globals.values.base_url}}pipeline-page/pipeline-page.html"
></app-route>
<app-route
id="cost_details"
path="/ui/:context/pipeline/:property/:id/"
redirect="history/"
></app-route>
<app-route
path="/ui/:context/pipeline/:property/:id/history/"
import="{{$.globals.values.base_url}}cost-page/cost-page.html"
new-page
></app-route>
</app-router>
<app-router id="appRouter" mode="pushstate" init="manual"> UI v3
<app-route
id="pipeline"
path$="[[globals.pathPrefix]]/:context/pipeline/"
element="pipeline-page"
import="/components/pages/pipeline/pipeline-page.html"
></app-route>
<app-route
id="costPage"
path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/"
redirect="history/"
></app-route>
<app-route
id="costHistoryPage"
path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/history/"
element="cost-data-page"
import="/components/pages/cost-data/cost-data-page.html"
old-page
></app-route>
</app-router>
+ Suitable for any size applications
- Only same design
- Time delays between UI transitions
Page-by-page release
#4
#5
Let frontend developer
deliver UI independently
#5
Delivery of UI with “monorepo” approach
1. Make changes
2. Wait till the whole monorepo can be deployed
3. Deploy
#5
Use standalone repository
and continuous delivery pipeline
#5
Delivery of UI with “standalone repo” approach
1. Make changes
2. Wait till the whole monorepo can be deployed
3. Deploy
#5
#6
Let frontend developer
use remote backend
#6
UI changes with “local backend” approach
1. Update backend source with dependencies
2. Update storage data
3. Update environment
4. Try to figure out via guide/FAQ why backend is not working
5. Call backend developer who knows how to update this
6. Spend half hour together to find stupid environment problem
7. Repeat 1-6 for each microservice
8. Make changes to frontend
#6
UI changes with “remote backend” approach
1. Update backend source with dependencies
2. Update storage data
3. Update environment
4. Try to figure out via guide/FAQ why backend is not working
5. Call backend developer who knows how to update this
6. Spend half hour together to find stupid environment problem
7. Repeat 1-6 for each microservice
8. Make changes to frontend
#6
request identity token
How to handle authorization?
identity token (JSON Web Token)
Backend
Attach header to each further request
Authorization: Bearer {identity token}
Frontend
#6
#6
#6
#6
#6
#6
#6
jwt.io
JSON Web Tokens libraries and debugger
#6
#7
Let frontend developer
don't deal with CORS
#7
#7
UI – https://domain.com/
https://foo.appspot.com/ https://bar.heroku.com/
Cross-origin resource sharing ?
#7
CORS (Cross-origin resource sharing)
● Request headers
○ Origin
○ Access-Control-Request-Method
○ Access-Control-Request-Headers
● Response headers
○ Access-Control-Allow-Origin
○ Access-Control-Allow-Credentials
○ Access-Control-Expose-Headers
○ Access-Control-Max-Age
○ Access-Control-Allow-Methods
○ Access-Control-Allow-Headers
#7
Use your web server’s ability
to serve UI and microservices
from single domain
#7
server {
server_name domain.com;
location /api/foo/ {
proxy_pass https://foo.appspot.com/;
}
location /api/bar/ {
proxy_pass https://bar.heroku.com/;
}
}
Nginx example
#7
#8
Let frontend developer
use production web server
without digging into its configuration
#8
Nginx
location /download/ {
valid_referers none blocked server_names *.example.com;
if ($invalid_referer) {
#rewrite ^/ http://www.example.com/
return 403;
}
# rewrite_log on;
# rewrite /download/*/mp3/*.any_ext to /download/*/mp3/*.mp3
rewrite ^/(download/.*)/mp3/(.*)..*$ /$1/mp3/$2.mp3 break;
root /spool/www;
#autoindex on;
access_log /var/log/nginx-download.access_log download;
}
Do you really want to learn and debug
production web server configuration?..
#8
Why “dev web server” approach may fail?
1. HTTP caching issues
2. Security issues
3. Missing redirects
#8
Use production-like Docker containers
with your local configuration
#8
.env
#8
DOMAIN=domain.com
FOO_ENDPOINT_URL=/api/foo
FOO_REAL_ENDPOINT_URL=https://foo.appspot.com/
BAR_ENDPOINT_URL=/api/bar
BAR_REAL_ENDPOINT_URL=https://bar.heroku.com/
...
.env
#8
.env
docker-compose
.override.yml
#8
version: '2'
services:
init:
environment:
FOO_REAL_ENDPOINT_URL: http://foo.custom
serve:
extra_hosts:
- "foo.custom:172.16.0.95"
docker-compose.override.yml
#8
.env
docker-compose
.override.yml
Configuration
$ docker-compose up init
Configured web server
$ docker-compose up -d serve
#8
#9
Let frontend developer
use production state
of concrete user
#9
How to fix a bug?
1. Prepare state
2. Reproduce
3. Fix
#9
● manual actions
● accesses to services
● historical entities
● data uniqueness
Let frontend developer
receive identity token of concrete user
but with read-only(!) permissions
#9
request identity token
“Looks like” feature
identity token of another user (read-only)
Backend
Attach header to each further request
Authorization: Bearer {identity token}
Frontend
#9
Frontend
developer
configure
#9
Recap
Let frontend developer:
1. don't deal with content management
2. use HTML/JS templating
3. don't deal with backend routing
4. don't deal with stagnated framework
5. deliver UI independently
6. use remote backend
7. don't deal with CORS
8. use production web server without digging into its configuration
9. use production state of concrete user
Questions?

More Related Content

KEY
HTML5 vs Silverlight
PDF
Continuous Integration and Deployment Patterns for Magento
PPTX
Magento NodeJS Microservices — Yegor Shytikov | Magento Meetup Online #11
PDF
Play Framework on Google App Engine - Productivity Stack
PDF
Rock-solid Magento Development and Deployment Workflows
PDF
Webpack Tutorial, Uppsala JS
PPTX
High Performance JavaScript (CapitolJS 2011)
PDF
Single page apps with drupal 7
HTML5 vs Silverlight
Continuous Integration and Deployment Patterns for Magento
Magento NodeJS Microservices — Yegor Shytikov | Magento Meetup Online #11
Play Framework on Google App Engine - Productivity Stack
Rock-solid Magento Development and Deployment Workflows
Webpack Tutorial, Uppsala JS
High Performance JavaScript (CapitolJS 2011)
Single page apps with drupal 7

What's hot (20)

PDF
Rock-solid Magento Deployments (and Development)
PPTX
An Intro into webpack
PDF
Optimising Your Front End Workflow With Symfony, Twig, Bower and Gulp
PDF
Magento with Composer
PPTX
High Performance Snippets
PPTX
What is HTML 5?
PDF
Advanced front-end automation with npm scripts
PDF
Webpack DevTalk
PDF
Hitchhiker's guide to the front end development
PDF
webpack 101 slides
PDF
Webpack: from 0 to 2
PDF
When Web meet Native App
PDF
Puppeteer - A web scraping & UI Testing Tool
PDF
Modern Web Application Development Workflow - EclipseCon France 2014
PDF
Npm scripts
PDF
High Performance JavaScript - jQuery Conference SF Bay Area 2010
PPT
Js unit testing
PPTX
Your Script Just Killed My Site
PDF
Clojure Web Development
PDF
WPE WebKit for Android
Rock-solid Magento Deployments (and Development)
An Intro into webpack
Optimising Your Front End Workflow With Symfony, Twig, Bower and Gulp
Magento with Composer
High Performance Snippets
What is HTML 5?
Advanced front-end automation with npm scripts
Webpack DevTalk
Hitchhiker's guide to the front end development
webpack 101 slides
Webpack: from 0 to 2
When Web meet Native App
Puppeteer - A web scraping & UI Testing Tool
Modern Web Application Development Workflow - EclipseCon France 2014
Npm scripts
High Performance JavaScript - jQuery Conference SF Bay Area 2010
Js unit testing
Your Script Just Killed My Site
Clojure Web Development
WPE WebKit for Android
Ad

Similar to Max Voloshin - "Organization of frontend development for products with microservices" (20)

PDF
Refactoring to a Single Page Application
PPTX
Refactoring to a SPA
PPTX
How do we drive tech changes
PPTX
Chandu.pptxhuuuuuuyyygfddssdfyuijhgghujjhhhhg
PPTX
Micro Front Ends : Divided We Rule by Parth Ghiya - AhmedabadJS
PPTX
Micro-Frontends JSVidCon
PPTX
Building assets on the fly with Node.js
PDF
Top 10 Latest Website Development Trends.pdf
PDF
Bd conf sencha touch workshop
PDF
Mastering Frontend Development A Comprehensive Guide To Learn Frontend Develo...
PDF
micro-frontends-with-vuejs
PDF
Tech Thursdays: Building Products
DOCX
Understanding Front-End Development: Skills, Tools, and Trends
PDF
JSFest 2019: Technology agnostic microservices at SPA frontend
PPTX
“Micro Frontends”- You Keep Using That Word, I Don’t Think It Means What You ...
PDF
Architectures For Scaling Ajax
PDF
The Rise of BaaS A Utopia for Client-Side Developers
PDF
Web fundamentals
PDF
Frontend developer Roadmap .pdf
PDF
Single Page Web Apps
Refactoring to a Single Page Application
Refactoring to a SPA
How do we drive tech changes
Chandu.pptxhuuuuuuyyygfddssdfyuijhgghujjhhhhg
Micro Front Ends : Divided We Rule by Parth Ghiya - AhmedabadJS
Micro-Frontends JSVidCon
Building assets on the fly with Node.js
Top 10 Latest Website Development Trends.pdf
Bd conf sencha touch workshop
Mastering Frontend Development A Comprehensive Guide To Learn Frontend Develo...
micro-frontends-with-vuejs
Tech Thursdays: Building Products
Understanding Front-End Development: Skills, Tools, and Trends
JSFest 2019: Technology agnostic microservices at SPA frontend
“Micro Frontends”- You Keep Using That Word, I Don’t Think It Means What You ...
Architectures For Scaling Ajax
The Rise of BaaS A Utopia for Client-Side Developers
Web fundamentals
Frontend developer Roadmap .pdf
Single Page Web Apps
Ad

More from IT Event (20)

PDF
Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...
PDF
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
PDF
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
PDF
Konstantin Krivlenia - "Continuous integration for frontend"
PPTX
Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"
PDF
Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"
PDF
Vladimir Grinenko - "Dependencies in component web done right"
PDF
Dmitry Bartalevich - "How to train your WebVR"
PDF
Aleksey Bogachuk - "Offline Second"
PDF
James Allardice - "Building a better login with the credential management API"
PDF
Fedor Skuratov "Dark Social: as messengers change the market of social media ...
PPTX
Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"
PPTX
Алексей Рагозин "Java и linux борьба за микросекунды"
PPTX
Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"
PDF
Наш ответ Uber’у
PDF
Александр Крашенинников "Hadoop High Availability: опыт Badoo"
PDF
Leonid Vasilyev "Building, deploying and running production code at Dropbox"
PDF
Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...
PDF
Mete Atamel "Resilient microservices with kubernetes"
PDF
Andrew Stain "User acquisition"
Denis Radin - "Applying NASA coding guidelines to JavaScript or airspace is c...
Sara Harkousse - "Web Components: It's all rainbows and unicorns! Is it?"
Roman Romanovsky, Sergey Rak - "JavaScript в IoT "
Konstantin Krivlenia - "Continuous integration for frontend"
Illya Klymov - "Vue.JS: What did I swap React for in 2017 and why?"
Evgeny Gusev - "A circular firing squad: How technologies drag frontend down"
Vladimir Grinenko - "Dependencies in component web done right"
Dmitry Bartalevich - "How to train your WebVR"
Aleksey Bogachuk - "Offline Second"
James Allardice - "Building a better login with the credential management API"
Fedor Skuratov "Dark Social: as messengers change the market of social media ...
Андрей Зайчиков "Архитектура распределенных кластеров NoSQL на AWS"
Алексей Рагозин "Java и linux борьба за микросекунды"
Volodymyr Lyubinets "Introduction to big data processing with Apache Spark"
Наш ответ Uber’у
Александр Крашенинников "Hadoop High Availability: опыт Badoo"
Leonid Vasilyev "Building, deploying and running production code at Dropbox"
Анатолий Пласковский "Миллионы карточных платежей за месяц, или как потерять ...
Mete Atamel "Resilient microservices with kubernetes"
Andrew Stain "User acquisition"

Recently uploaded (20)

PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PDF
Modernizing your data center with Dell and AMD
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Machine learning based COVID-19 study performance prediction
PDF
Unlocking AI with Model Context Protocol (MCP)
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Spectral efficient network and resource selection model in 5G networks
PPT
Teaching material agriculture food technology
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
NewMind AI Monthly Chronicles - July 2025
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PPTX
A Presentation on Artificial Intelligence
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
Modernizing your data center with Dell and AMD
Agricultural_Statistics_at_a_Glance_2022_0.pdf
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Machine learning based COVID-19 study performance prediction
Unlocking AI with Model Context Protocol (MCP)
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Dropbox Q2 2025 Financial Results & Investor Presentation
Spectral efficient network and resource selection model in 5G networks
Teaching material agriculture food technology
Chapter 3 Spatial Domain Image Processing.pdf
Digital-Transformation-Roadmap-for-Companies.pptx
NewMind AI Monthly Chronicles - July 2025
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
A Presentation on Artificial Intelligence
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Diabetes mellitus diagnosis method based random forest with bat algorithm

Max Voloshin - "Organization of frontend development for products with microservices"

  • 2. Max Voloshin ● Location: Dnipro, Ukraine ● Position: Team Lead, OWOX ● In professional software development since 2010 ● Fan of microservices and continuous deployment Photographer: Avilov Alexandr
  • 3. OWOX Business Intelligence As business: ● More than 1 million transactions per week in our clients' projects ● 1 000+ projects in 50 countries rely on our solutions ● $ 200 000+ daily handled our clients' costs on advertising As software: ● 10+ microservices in PHP/Java ● Web Components (Polymer 1.8) ● Several releases every day
  • 4. Why am I here? I want to share 9 improvements for making frontend development easier
  • 5. Who cares? Frontend development may be a constraint for your product
  • 7. #1
  • 8. Let frontend developer don't deal with content management #1
  • 9. <div class="e-wrap"> <div class="e-grid-col1 e-grid-col-empty"></div> <div class="e-grid-col8" role="main"> <h2 class="e-page-title"> Page not found </h2> <p class="error-message"> Incorrectly typed address or a page on the website no longer exists </p> <p class="error-link-wrap"><a href="/" class="error-link"> Home </a></p> </div> <div class="clear"></div> </div>
  • 10. Do you really want to change one selling text to another?.. #1
  • 11. Use mnemonic names of texts instead of hard-coded content #1
  • 12. Admin panel for content Write contentContent manager Frontend developer Frontend Write code Matched by mnemonic name #1
  • 13. Polymer <div class="e-wrap"> <div class="e-grid-col1 e-grid-col-empty"></div> <div class="e-grid-col8" role="main"> <h2 class="e-page-title"> <html-text name="NotFoundPage.Title"></html-text> </h2> <p class="error-message"> <html-text name="NotFoundPage.Message"></html-text> </p> <p class="error-link-wrap"><a href="/" class="error-link"> <html-text name="HomePage.Title"></html-text> </a></p> </div> </div>
  • 15. #2
  • 16. Let frontend developer use HTML/JS templating #2
  • 17. .answers-i-link:before { CSS as Smarty (PHP) template content: ""; position: absolute; left: -30px; width: 25px; height: 25px; background: url('/*{$settings.path}*//icons.png') 0 -414px no-repeat; } .answers-i-link:hover:before { background: url('/*{$settings.path}*//icons.png') 0 -373px no-repeat; } .answers-i-link.active:before { background: url('/*{$settings.path}*//icons.png') 0 -700px no-repeat; } .answers-i-link.active:hover:before { background: url('/*{$settings.path}*//icons.png') 0 -659px no-repeat; }
  • 18. {foreach $tree as $section} HTML as Smarty (PHP) template <h3 class="level-{$level}">{$section['title']}</h3> {if $section['children']} {include file='faq/subjects.tpl' tree=$section['children'] level=$level+1} {/if} {if isset($section['records'])} {foreach $section['records'] as $record} {if $is_contents} <a href="#record-{$record.id}" class="level-{$level}">{$record.title}</a><br /> {else} <a name="record-{$record.id}" class="level-{$level}">{$record.title}</a><br /> {$record['answer'] nofilter} {/if} {/foreach} {/if} {/foreach}
  • 19. HTML as Smarty (PHP) template <div class="b-expenses-head"> {foreach from=$manual_costs item="month" name="tabs"} <div id="manual-costs-tab-{$smarty.foreach.tabs.index}" class="inline b-expenses-head-month {if $month['is_active']}active{/if}"> <span class="b-expenses-month-title">{$month['month_title']}</span> </div> <script type="text/javascript"> ManualCosts.addGroup(new ManualCostsTab_class({$smarty.foreach.tabs.index}, '{$month['hash']}')); </script> {/foreach} </div>
  • 20. JS as Smarty (PHP) template initialize: function(data, connect_service_access) { this.parent(data, connect_service_access); Object.append( this._templates, {access_create: '/*{template_script_fetch file="access_create.jst" }*/'} ); this._popup.addEvent('createAccess', this.onCreateAccess.bind(this)); }, onCreateAccess: function() { this.send( '/*{$menu.my.href}*/users/access/simple_services#createAccess', { service_name: this._data['service_name'], token: token, service_account: service_account } ); }
  • 21. Do you really want to learn and debug non JS/HTML templating?.. #2
  • 22. Use HTML/JS templating, use data as JSON #2
  • 23. Polymer <template is="dom-if" if="[[!isEmpty(url)]]"> <page-item item-action> <a is="pushstate-anchor" href="[[url]]" class="item-action"> <text-html name="Page.Item.Actions.Edit"></text-html> </a> </page-item> </template>
  • 24. Polymer <dom-module id="page-external-link-styles"> <template> <style include="shared-styles"> :host { display: inline; white-space: nowrap; } :host(:not(.no-link-style)) a { color: var(--blue-color-main); text-decoration: none; } </style> </template> </dom-module>
  • 25. Bonus: static code analysis & tests #2
  • 26. #3
  • 27. Let frontend developer don't deal with backend routing #3
  • 28. Symfony (PHP) routing # app/config/routing.yml blog_list: path: /blog/{page} defaults: { _controller: AppBundle:Blog:list, page: 1 } requirements: page: 'd+' blog_show: # ...
  • 29. Do you really want to learn and debug backend routing?.. #3
  • 30. Use frontend routing via Single Page Application #3
  • 31. Polymer <app-router id="appRouter" mode="pushstate" init="manual"> <app-route id="dataPipeline" path$="[[globals.pathPrefix]]/:context/pipeline/" element="pipeline-page" import="/components/pages/pipeline/pipeline-page.html" ></app-route> <app-route id="costDataPage" path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/" redirect="history/" ></app-route> <app-route id="costDataHistoryPage" path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/history/" element="cost-data-page" import="/components/pages/cost-data/cost-data-page.html" ></app-route> </app-router>
  • 32. #4
  • 33. Let frontend developer don't deal with stagnated framework #4
  • 34. Switching framework is hard ● You can’t pause product development ● You can't release all changes at one time #4
  • 36. 1. Continuous deploy then single release #4
  • 37. UI v1 (MooTools) → UI v2 (Polymer 0.5) t Release v1 Deploy v2 (part #1) Change v1 prod Deploy v2 (part #2) Release v2 Deploy v2 (part #N) ... Deploy != Release #4 UI v1: https://domain.com/ UI v2: https://domain.com/v2/ ← SPA root
  • 38. Continuous deploy then single release + Compatible with design switching - Suitable only for small applications because of effort duplication #4
  • 39. Off topic: why Polymer? ● Material Design out of the box ● Possibilities to UI reuse (not only JS) ● Google promotion of Web Components ● #UseThePlatform #4
  • 41. The first law of holes #4
  • 42. Limitations: 1. Only one version of Polymer can be on page 2. We have Single Page Application Solution: “break points” in Single Page Application UI v2 — domain.com/v2/ UI v3 — domain.com/v3/ /foo/ /bar/ /baz/ /qux/ /foo/ /bar/ /baz/ /qux/ UI v2 (Polymer 0.5) → UI v3 (Polymer 1.8) #4
  • 43. UI v2 <app-router id="router" mode="pushstate" init="manual" <app-route id="pipeline" path="/ui/:context/pipeline/" import="{{$.globals.values.base_url}}pipeline-page/pipeline-page.html" ></app-route> <app-route id="cost_details" path="/ui/:context/pipeline/:property/:id/" redirect="history/" ></app-route> <app-route path="/ui/:context/pipeline/:property/:id/history/" import="{{$.globals.values.base_url}}cost-page/cost-page.html" new-page ></app-route> </app-router>
  • 44. <app-router id="appRouter" mode="pushstate" init="manual"> UI v3 <app-route id="pipeline" path$="[[globals.pathPrefix]]/:context/pipeline/" element="pipeline-page" import="/components/pages/pipeline/pipeline-page.html" ></app-route> <app-route id="costPage" path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/" redirect="history/" ></app-route> <app-route id="costHistoryPage" path$="[[globals.pathPrefix]]/:context/pipeline/:property/:id/history/" element="cost-data-page" import="/components/pages/cost-data/cost-data-page.html" old-page ></app-route> </app-router>
  • 45. + Suitable for any size applications - Only same design - Time delays between UI transitions Page-by-page release #4
  • 46. #5
  • 47. Let frontend developer deliver UI independently #5
  • 48. Delivery of UI with “monorepo” approach 1. Make changes 2. Wait till the whole monorepo can be deployed 3. Deploy #5
  • 49. Use standalone repository and continuous delivery pipeline #5
  • 50. Delivery of UI with “standalone repo” approach 1. Make changes 2. Wait till the whole monorepo can be deployed 3. Deploy #5
  • 51. #6
  • 52. Let frontend developer use remote backend #6
  • 53. UI changes with “local backend” approach 1. Update backend source with dependencies 2. Update storage data 3. Update environment 4. Try to figure out via guide/FAQ why backend is not working 5. Call backend developer who knows how to update this 6. Spend half hour together to find stupid environment problem 7. Repeat 1-6 for each microservice 8. Make changes to frontend #6
  • 54. UI changes with “remote backend” approach 1. Update backend source with dependencies 2. Update storage data 3. Update environment 4. Try to figure out via guide/FAQ why backend is not working 5. Call backend developer who knows how to update this 6. Spend half hour together to find stupid environment problem 7. Repeat 1-6 for each microservice 8. Make changes to frontend #6
  • 55. request identity token How to handle authorization? identity token (JSON Web Token) Backend Attach header to each further request Authorization: Bearer {identity token} Frontend #6
  • 56. #6
  • 57. #6
  • 58. #6
  • 59. #6
  • 60. #6
  • 61. #6
  • 62. jwt.io JSON Web Tokens libraries and debugger #6
  • 63. #7
  • 64. Let frontend developer don't deal with CORS #7
  • 65. #7
  • 66. UI – https://domain.com/ https://foo.appspot.com/ https://bar.heroku.com/ Cross-origin resource sharing ? #7
  • 67. CORS (Cross-origin resource sharing) ● Request headers ○ Origin ○ Access-Control-Request-Method ○ Access-Control-Request-Headers ● Response headers ○ Access-Control-Allow-Origin ○ Access-Control-Allow-Credentials ○ Access-Control-Expose-Headers ○ Access-Control-Max-Age ○ Access-Control-Allow-Methods ○ Access-Control-Allow-Headers #7
  • 68. Use your web server’s ability to serve UI and microservices from single domain #7
  • 69. server { server_name domain.com; location /api/foo/ { proxy_pass https://foo.appspot.com/; } location /api/bar/ { proxy_pass https://bar.heroku.com/; } } Nginx example #7
  • 70. #8
  • 71. Let frontend developer use production web server without digging into its configuration #8
  • 72. Nginx location /download/ { valid_referers none blocked server_names *.example.com; if ($invalid_referer) { #rewrite ^/ http://www.example.com/ return 403; } # rewrite_log on; # rewrite /download/*/mp3/*.any_ext to /download/*/mp3/*.mp3 rewrite ^/(download/.*)/mp3/(.*)..*$ /$1/mp3/$2.mp3 break; root /spool/www; #autoindex on; access_log /var/log/nginx-download.access_log download; }
  • 73. Do you really want to learn and debug production web server configuration?.. #8
  • 74. Why “dev web server” approach may fail? 1. HTTP caching issues 2. Security issues 3. Missing redirects #8
  • 75. Use production-like Docker containers with your local configuration #8
  • 80. .env docker-compose .override.yml Configuration $ docker-compose up init Configured web server $ docker-compose up -d serve #8
  • 81. #9
  • 82. Let frontend developer use production state of concrete user #9
  • 83. How to fix a bug? 1. Prepare state 2. Reproduce 3. Fix #9 ● manual actions ● accesses to services ● historical entities ● data uniqueness
  • 84. Let frontend developer receive identity token of concrete user but with read-only(!) permissions #9
  • 85. request identity token “Looks like” feature identity token of another user (read-only) Backend Attach header to each further request Authorization: Bearer {identity token} Frontend #9 Frontend developer configure
  • 86. #9
  • 87. Recap Let frontend developer: 1. don't deal with content management 2. use HTML/JS templating 3. don't deal with backend routing 4. don't deal with stagnated framework 5. deliver UI independently 6. use remote backend 7. don't deal with CORS 8. use production web server without digging into its configuration 9. use production state of concrete user