Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building Modular Frontend Architectures

Steve Kinney
August 07, 2019
400

Building Modular Frontend Architectures

Steve Kinney

August 07, 2019
Tweet

Transcript

  1. BUILDING MODULAR FRONTEND
    ARCHITECTURES
    Steve Kinney
    Senior Principal Engineer
    Twilio

    View full-size slide

  2. TABLE OF
    CONTENTS
    01. Defining Modularity
    02. Layers of an Application
    03. Creating Modular UI
    04. Modular Deployment

    View full-size slide

  3. BUT, FIRST—WHO AM I?

    View full-size slide

  4. AND WHO ARE YOU?

    View full-size slide

  5. WHAT DO WE MEAN BY MODULAR?

    View full-size slide

  6. SEPARATION IS NOT MODULARITY

    View full-size slide

  7. MODULARITY DOES NOT HAPPEN
    BY ACCIDENT.

    View full-size slide

  8. WHAT ARE SOME OF THE MOVING PARTS?
    • User Interface
    • State Management
    • Location Within the Application
    • Communication with the Server

    View full-size slide

  9. PATTERNS FOR APPLICATIONS
    • Model
    • View
    • Controller

    View full-size slide

  10. PATTERNS FOR WEB APPLICATIONS
    • Model
    • View
    • Controller
    • Routing
    • Data Fetching

    View full-size slide

  11. MICROSERVICE VERSUS MODULAR
    ARCHITECTURES

    View full-size slide

  12. MICRO-FRONTEND VERSUS
    MODULAR ARCHITECTURES

    View full-size slide

  13. Micro-Frontend One
    Micro-Frontend Two
    Build Pipeline
    Build Pipeline
    Micro-Frontend One
    Micro-Frontend Two
    Application Shell

    View full-size slide

  14. STARTING SMALL

    View full-size slide

  15. SINGLE RESPONSIBILITY
    A given component or module of your
    application should have only one reason why
    you’d need to change it.

    View full-size slide

  16. class CampaignsList extends React.Component {
    state = { campaigns: [] };
    componentDidMount() {
    getCampaignsFromApi()
    .then(campaigns !=> this.setState({ campaigns }));
    }
    render() {

    { this.state.campaigns.map(campaign !=> { }) }
    !
    }
    }

    View full-size slide

  17. class CampaignsFetcher extends React.Component {
    state = { campaigns: [] };
    componentDidMount() {
    getCampaignsFromApi()
    .then(campaigns !=> this.setState({ campaigns }));
    }
    render() {
    ;
    }
    }
    const CampaignsList = ({ campaigns }) !=> (

    {campaigns.map(campaign !=> {
    ;
    })}
    !
    );

    View full-size slide

  18. TAKING THIS PATTERN A STEP FURTHER
    • Tired: Multiple responsibilities
    • Wired: Separating responsibilities
    • Inspired: Creating reusable, higher-order components

    View full-size slide

  19. const withCampaigns = (WrappedComponent) !=> {
    return class extends React.Component {
    state = { campaigns: [] };
    componentDidMount() {
    getCampaignsFromApi()
    .then(campaigns !=> this.setState({ campaigns }));
    }
    render() {
    return
    }
    }
    };
    const CampaignsList = ({ campaigns }) !=> (

    {campaigns.map(campaign !=> {
    ;
    })}
    !
    );
    withCampaigns(CampaignsList);

    View full-size slide

  20. withCurrentUser(withCampaigns(CampaignsList));

    View full-size slide

  21. SEPARATING STATE FROM THE UI

    View full-size slide

  22. Reducer
    New State
    State
    Action
    User Interface

    View full-size slide

  23. {
    TYPE: 'MESSAGE_SAVE',
    PAYLOAD: { … }
    }

    View full-size slide

  24. const mapStateToProps = (state) !=> {
    return state.campaigns;
    };
    const mapDispatchToProps = {
    deleteCampaign,
    getCampaignStats,
    };
    connect(mapStateToProps, mapDispatchToProps)(Campaigns);

    View full-size slide

  25. WHEN YOU INTERACT WITH THE UI,
    IT SIMPLY FIRES ACTIONS.

    View full-size slide

  26. REDUX DOES NOT HAVE ANY
    CONCEPT OF DATA FETCHING.

    View full-size slide

  27. API Data Adapter User Interface Data

    View full-size slide

  28. THE URL IS A FUNDAMENTAL PART
    OF THE WAY THE WEB WORKS.

    View full-size slide

  29. SINGLE-SENDS/:ID/EDITOR/PREVIEW

    View full-size slide

  30. SINGLE-SENDS/:ID/DELETE

    View full-size slide

  31. routeFor('single-sends');
    routeFor('single-sends', id);
    RouteRegistry.add('multi-sends', '/multi-sends');

    View full-size slide

  32. BUILDING COMPOSABLE COMPONENTS

    View full-size slide

  33. WHAT IS COMPOSABILITY?

    View full-size slide

  34. 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

    View full-size slide

  35. PREFER DEPENDENCY INJECTION TO
    IMPORTING

    View full-size slide

  36. Configuration Component

    View full-size slide

  37. content={singleSendHtml}
    onChange={updateSingleSendContent}
    hasImageManager={true}
    !/>

    View full-size slide

  38. const configuration = {
    colorTheme: {
    baseName: 'FlexDark'
    }
    };
    React.createContext(configuration);

    View full-size slide

  39. 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

    View full-size slide

  40. BECAUSE WE’VE ENCAPSULATED
    OUR MODULES AND MADE THEM
    COMPOSABLE, OUR APPLICATION IS
    EXTENSIBLE.

    View full-size slide

  41. Flex.Component.Content.add(
    ,
    { options }
    );
    Flex.Component.Content.replace(
    ,
    { options }
    );

    View full-size slide

  42. MODULARITY AND PERFORMANCE

    View full-size slide

  43. “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

    View full-size slide

  44. import('./filename')
    .then(() !=> …);

    View full-size slide

  45. export const Routes = (












    !
    );

    View full-size slide

  46. import Loadable from 'react-loadable';
    import LoadingPage from '!../LoadingPage';
    export default Loadable({
    loader: () !=> import('./components/page'),
    loading: LoadingPage,
    });

    View full-size slide

  47. THANK YOU +
    QUESTIONS?

    View full-size slide