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
130
React_Performance__2022.pdf
stevekinney
0
36
React Performance v2
stevekinney
0
59
Introduction to Testing
stevekinney
0
180
Web Security, Frontend Masters
stevekinney
0
4.1k
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
230
Featured
See All Featured
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
45k
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2.1k
Designing Powerful Visuals for Engaging Learning
tmiket
1
470
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
2.2k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.3k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
270
BBQ
matthewcrist
89
10k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
330
It's Worth the Effort
3n
188
29k
Facilitating Awesome Meetings
lara
57
7.1k
Code Reviewing Like a Champion
maltzj
528
40k
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?