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
Building Modular Frontend Architectures
Search
Steve Kinney
August 07, 2019
500
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Building Modular Frontend Architectures
Steve Kinney
August 07, 2019
More Decks by Steve Kinney
See All by Steve Kinney
Enterprise UI, v2
stevekinney
0
83
React_Performance__2022.pdf
stevekinney
0
34
React Performance v2
stevekinney
0
52
Introduction to Testing
stevekinney
0
170
Web Security, Frontend Masters
stevekinney
0
4k
Making Music with the Web Audio API, JSConf Colombia 2023
stevekinney
0
130
React and TypeScript, Turing School
stevekinney
0
380
Redux Workshop, 2021-05-05
stevekinney
2
2.2k
TypeScript and React Utility Types
stevekinney
1
220
Featured
See All Featured
Reality Check: Gamification 10 Years Later
codingconduct
0
2.2k
Paper Plane
katiecoart
PRO
2
52k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
180
How to Ace a Technical Interview
jacobian
281
24k
The Cult of Friendly URLs
andyhume
79
6.9k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
1.9k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6k
Un-Boring Meetings
codingconduct
0
340
Exploring anti-patterns in Rails
aemeredith
3
440
Automating Front-end Workflow
addyosmani
1370
210k
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
200
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
Transcript
BUILDING MODULAR FRONTEND ARCHITECTURES Steve Kinney Senior Principal Engineer Twilio
TABLE OF CONTENTS 01. Defining Modularity 02. Layers of an
Application 03. Creating Modular UI 04. Modular Deployment
BUT, FIRST—WHO AM I?
AND WHO ARE YOU?
None
WHAT DO WE MEAN BY MODULAR?
None
None
SEPARATION IS NOT MODULARITY
MODULARITY DOES NOT HAPPEN BY ACCIDENT.
WHAT ARE SOME OF THE MOVING PARTS? • User Interface
• State Management • Location Within the Application • Communication with the Server
PATTERNS FOR APPLICATIONS • Model • View • Controller
PATTERNS FOR WEB APPLICATIONS • Model • View • Controller
• Routing • Data Fetching
MICROSERVICE VERSUS MODULAR ARCHITECTURES
MICRO-FRONTEND VERSUS MODULAR ARCHITECTURES
Micro-Frontend One Micro-Frontend Two Build Pipeline Build Pipeline Micro-Frontend One
Micro-Frontend Two Application Shell
STARTING SMALL
SINGLE RESPONSIBILITY A given component or module of your application
should have only one reason why you’d need to change it.
class CampaignsList extends React.Component { state = { campaigns: []
}; componentDidMount() { getCampaignsFromApi() .then(campaigns !=> this.setState({ campaigns })); } render() { <section> { this.state.campaigns.map(campaign !=> { <Campaign campaign={campaign} !/> }) } !</section> } }
class CampaignsFetcher extends React.Component { state = { campaigns: []
}; componentDidMount() { getCampaignsFromApi() .then(campaigns !=> this.setState({ campaigns })); } render() { <Campaigns campaigns={campaigns} !/>; } } const CampaignsList = ({ campaigns }) !=> ( <section> {campaigns.map(campaign !=> { <Campaign campaign={campaign} !/>; })} !</section> );
TAKING THIS PATTERN A STEP FURTHER • Tired: Multiple responsibilities
• Wired: Separating responsibilities • Inspired: Creating reusable, higher-order components
const withCampaigns = (WrappedComponent) !=> { return class extends React.Component
{ state = { campaigns: [] }; componentDidMount() { getCampaignsFromApi() .then(campaigns !=> this.setState({ campaigns })); } render() { return <WrappedComponent campaigns={campaigns} !/> } } }; const CampaignsList = ({ campaigns }) !=> ( <section> {campaigns.map(campaign !=> { <Campaign campaign={campaign} !/>; })} !</section> ); withCampaigns(CampaignsList);
withCurrentUser(withCampaigns(CampaignsList));
SEPARATING STATE FROM THE UI
None
Reducer New State State Action User Interface
{ TYPE: 'MESSAGE_SAVE', PAYLOAD: { … } }
const mapStateToProps = (state) !=> { return state.campaigns; }; const
mapDispatchToProps = { deleteCampaign, getCampaignStats, }; connect(mapStateToProps, mapDispatchToProps)(Campaigns);
WHEN YOU INTERACT WITH THE UI, IT SIMPLY FIRES ACTIONS.
REDUX DOES NOT HAVE ANY CONCEPT OF DATA FETCHING.
None
API Data Adapter User Interface Data
None
THE URL IS A FUNDAMENTAL PART OF THE WAY THE
WEB WORKS.
SINGLE-SENDS/:ID/EDITOR/PREVIEW
SINGLE-SENDS/:ID/DELETE
routeFor('single-sends'); routeFor('single-sends', id); RouteRegistry.add('multi-sends', '/multi-sends');
BUILDING COMPOSABLE COMPONENTS
WHAT IS COMPOSABILITY?
None
None
None
Automation Design Editor Side Panel Top Navigation Drag and Drop
Module Editor Preview Settings Tab Build Tab Tags Tab Desktop Preview Mobile Preview Plain Text Preview
PREFER DEPENDENCY INJECTION TO IMPORTING
Configuration Component
<DragAndDropEditor content={singleSendHtml} onChange={updateSingleSendContent} hasImageManager={true} !/>
const configuration = { colorTheme: { baseName: 'FlexDark' } };
React.createContext(configuration);
Configuration in Context API Automations Index Page Automations Design Editor
Automations Code Editor Design Editor Drag and Drop Modules Design Editor Preview HTML Editor Code Editor Preview
None
None
BECAUSE WE’VE ENCAPSULATED OUR MODULES AND MADE THEM COMPOSABLE, OUR
APPLICATION IS EXTENSIBLE.
Flex.Component.Content.add( <MyComponent key="demo-component"!/>, { options } ); Flex.Component.Content.replace( <MyComponent key="demo-component"!/>,
{ options } );
MODULARITY AND PERFORMANCE
“NETWORKS, CPUS, AND DISKS ALL HATE YOU. ON THE CLIENT,
YOU PAY FOR WHAT YOU SEND IN WAYS YOU CAN'T EASILY SEE.” ALEX RUSSEL, GOOGLE
None
None
None
import('./filename') .then(() !=> …);
export const Routes = ( <Fragment> <Route routeId="automations" path="/automations" component={Automations}!/>
<Route routeId="campaigns" path="/campaigns" component={Campaigns}!/> <Route routeId="contacts" path="/contacts" component={Contacts}!/> <Route routeId="custom-fields" path="/custom-fields" component={CustomFields}!/> <Route routeId="marketing-templates" path="/marketing-templates" component={MarketingTemplates}!/> <Route routeId="notifications" path="/notifications" component={Notifications}!/> <Route routeId="overview" path="/overview" component={Overview}!/> <Route routeId="senders" path="/senders" component={Senders}!/> <Route routeId="templates" path="/templates" component={Templates}!/> <Route routeId="tour" path="/tour" component={Tour}!/> <Route routeId="welcome" path="/welcome" component={Welcome}!/> !</Fragment> );
import Loadable from 'react-loadable'; import LoadingPage from '!../LoadingPage'; export default
Loadable({ loader: () !=> import('./components/page'), loading: LoadingPage, });
None
None
None
THANK YOU + QUESTIONS?