Playful Features ... Feature Based Development that "Plugs and Plays"

Playful Features ... Feature Based Development that "Plugs and Plays"

Feature-Driven Development (FDD) has become more prevalent in today's landscape, and for good reason! FDD is a lightweight Agile technique, manifest in a project structure where your code is organized by what it accomplishes (i.e. features), rather than lumping all modules of like types into separate blobs of components, routes, logic, actions, etc. This technique greatly improves your code comprehension because there is a direct correlation between the problem space (the requirements) and the implementation (the code)!

However, FDD involves more than just organizing your project's directory structure into features. You want to encapsulate your features into isolated and self-sufficient modules, and yet they must also be able to collaborate with other features.

Truly isolated FDD is something that is incredibly powerful! You can improve the modularity of your system by loosely coupling your features, making your app easier to understand, develop, test, and refactor. If done right, your features actually become "miniature applications" that simply plug-and-play (where the mere existence of a feature dynamically exudes the characteristics it implements)!

As it turns out there are a number of hurdles to overcome in order to accomplish this. Rather than being left to fend for yourself, a new utility called feature-u (https://feature-u.js.org/) has already tackled these hurdles (check out this teaser - http://bit.ly/feature-u-teaser).

feature-u promotes a new and unique approach to code organization and app orchestration.

With feature-u ...

- your features can be encapsulated and isolated
- they can collaborate with other features in an extendable way
- your components can employ cross-feature composition (even injecting their content autonomously)
- your features can initialize themselves
- they can be activated or deactivated at run-time
- and as a bonus, your frameworks will even auto-configure with only the active features (via a plugin architecture)

In short, your features can become more playful ... they can plug-and-play!

feature-u opens new doors into the exciting world of FDD. It frees you up to focus your attention on the "business end" of your features!

4745c10de93b07ff06f69648749b673f?s=128

Kevin Bridges

August 23, 2019
Tweet

Transcript

  1. 2.

    • Married, Father, Grandfather • 40 yrs in software (20

    yrs consulting) • Retired • Be nice to the old guy :-) Kevin Bridges Applied Software Technologies kevin@wiiBridges.com @kevinast https://bit.ly/feature-u-pres slides, syllabus, articles, docs, and repo!
  2. 5.
  3. 6.

    Goals • Requirements based • Encapsulation • Self Sufficient •

    Plug-and-Play Hurdles • Isolation vs. Collaboration • Start-Up Initialization • Framework Configuration • UI Composition • Feature Enablement In short, how do we achieve a running application from these isolated features?
  4. 7.

    Two Primary Tenets needed to Achieve our Goals 1. Feature

    Runtime Consolidation 2. Feature Collaboration
  5. 8.

    1.

  6. 9.

    2.

  7. 10.
  8. 11.
  9. 13.

    Authorization Base UI Eateries View Discovery View GPS Location PWA

    Dev Sandbox Ultimate Goal 2. Startup Initialization Encapsulation 1. Requirement Based Features 4. Feature Collaboration == Mini Apps 3. Feature Enablement
  10. 14.

    Covered in this Session: 1. Simplified App Startup a. App-Specific

    Initialization in Features b. Auto-Configuration of Frameworks 2. Feature Enablement 3. Cross-Feature Collaboration a. Cross-Feature Sharing b. Cross-Feature UI Composition 4. A/B Feature Swap 5. Plug-and-Play
  11. 19.
  12. 20.
  13. 21.
  14. 22.
  15. 24.

    appWillStart({fassets, curRootAppElm}): rootAppElm|void invoked early in the app startup process

    … supports accumulative static root DOM injection appDidStart({fassets, [appState, dispatch]}): void invoked immediately after app starts … triggers “app is running” processes Feature Encapsulation of App Startup appInit({showStatus, fassets, [appState, dispatch]}): promise|void invoked later in the app startup process … supports blocking async initialization 1. Feature Runtime Consolidation Application Life Cycle Hooks
  16. 28.

    1. Feature Runtime Consolidation import React from 'react'; import Expo

    from 'expo'; import {LayoutAnimation} from 'react-native'; import {launchApp} from 'feature-u'; import {createReducerAspect} from 'feature-redux'; import {createLogicAspect} from 'feature-redux-logic'; import {createRouteAspect} from 'feature-router'; import features from './feature'; import SplashScreen from './util/comp/SplashScreen'; // launch our application, exposing the feature-u Fassets object // ... facilitating cross-feature-communication! export default launchApp({ aspects: appAspects(), features, registerRootAppElm(rootAppElm) { Expo.registerRootComponent(()=>rootAppElm); // convert rootAppElm to a React Component } }); // accumulate/configure the Aspect plugins matching our app's run-time stack function appAspects() { // define our framework run-time stack const reducerAspect = createReducerAspect(); const logicAspect = createLogicAspect(); const routeAspect = createRouteAspect(); const aspects = [ reducerAspect, // redux ... extending: Feature.reducer logicAspect, // redux-logic ... extending: Feature.logic routeAspect, // Feature Routes ... extending: Feature.route ]; // configure Aspects (as needed) // ... StateRouter fallback screen (when no routes are in effect) routeAspect.config.fallbackElm$ = <SplashScreen msg="I'm trying to think but it hurts!"/>; // beam me up Scotty :-) return aspects; } src/app.js src/ │ app.js ... our mainline - launches app via launchApp() │ ├──feature/ │ │ index.js ... accumulate/promote all app Feature objects │ │ │ ├──auth/ ... the app's authorization feature │ │ │ actions.js │ │ │ fassets.js │ │ │ feature.js ... expose aspects of interest to feature-u │ │ │ featureName.js │ │ │ index.js │ │ │ logic.js │ │ │ route.js │ │ │ signInFormMeta.js │ │ │ state.js │ │ └──comp/ │ │ SignInScreen.js │ │ SignInVerifyScreen.js │ │ │ ├──currentView/ ... more features │ │ │ ├──device/ ... feature to initialize the device │ │ │ actions.js │ │ │ api.js │ │ │ appDidStart.js │ │ │ appWillStart.js │ │ │ fassets.js │ │ │ feature.js ... expose aspects of interest to feature-u │ │ │ featureName.js │ │ │ index.js │ │ │ logic.js │ │ │ route.js │ │ │ state.js │ │ └──init/ │ │ platformSetup.android.js │ │ platformSetup.ios.js │ │ │ ├──discovery/ ... more features │ ├──eateries/ │ ├──firebase/ │ ├──leftNav/ │ ├──logActions/ │ └──sandbox/ │ └──util/ ... common utilities used across all features directory structure import React from 'react'; import platformSetup from './init/platformSetup'; import Notify from '../../util/notify'; /** * An app-level life-cycle hook, initializing our feature by: * - performing platform-specific setup (iOS/Android) * - inject our notify utility in the root DOM */ export default function appWillStart({fassets, curRootAppElm}) { // platform-specific setup (iOS/Android) platformSetup(); // initialize notify utility, by injecting it in our App root return [React.Children.toArray(curRootAppElm), <Notify key="Notify"/>]; } src/feature/device/appWillStart.js import {createFeature} from 'feature-u'; import initFireBase from './init/initFireBase'; /** * The **'firebase'** feature initializes the google firebase service, * and provides a placeholder for future API abstractions. */ export default createFeature({ name: 'firebase', appWillStart({fassets, curRootAppElm}) { initFireBase(); // initialize FireBase }, }); src/feature/firebase/feature.js redux auto configured by feature-redux Aspect Plugin import React from 'react'; import {Drawer} from 'native-base'; import SideBar, {registerDrawer, closeSideBar} from './comp/SideBar'; /** * Inject our Drawer/SideBar component at the root of our app */ export default function appWillStart({fassets, curRootAppElm}) { return ( <Drawer ref={ ref => registerDrawer(ref) } content={<SideBar/>} onClose={closeSideBar}> {curRootAppElm} </Drawer> ); } src/feature/leftNav/appWillStart.js export default launchApp({ aspects: appAspects(), features, registerRootAppElm(rootAppElm) { Expo.registerRootComponent(()=>rootAppElm); } }); src/app.js import actions from './actions'; /** * An app-level life-cycle hook that dispatches our bootstrap action * that gets the ball rolling! */ export default function appDidStart({fassets, appState, dispatch}) { dispatch( actions.bootstrap() ); } src/feature/device/appDidStart.js Feature Encapsulation of App Startup • App Initialization (Application Life Cycle Hooks) • Framework Configuration (Extendable Aspect Plugins) Made possible because feature-u starts the app!
  17. 30.

    Feature Enablement export default createFeature({ name: 'sandbox’, enabled: inDevelopmentMode(), ...

    snip snip }); src/feature/sandbox/feature.js • by default all Features are active • can be disable via enabled Feature directive Feature Enablement Made possible because feature-u starts the app!
  18. 32.

    2. Feature Collaboration Cross Feature Communication fassets • fassets facilitate

    Cross Feature Communication • accommodates all Cross Feature Sharing • "Public Face" of a feature • Open • • SideBar: fasset (not facet) fassets – Feature Assets
  19. 34.

    2. Feature Collaboration Cross Feature Communication fassets export default createFeature({

    name: 'featureA', fassets: { define: { 'actions.openView': actions.view.open, // openView(viewName): Action 'sel.currentView': selector.currentView, // currentView(state): viewName 'sel.isDeviceReady': selector.isDeviceReady, // isDeviceReady(state): boolean }, }, ... }); defining fassets if (fassets.sel.isDeviceReady(appState)) { ... } using fassets NOTE: This uses a push philosophy code snippet ...
  20. 35.

    2. Feature Collaboration Feature Based UI Composition withFassets() export default

    createFeature({ name: 'common', fassets: { define: { 'company.logo': () => <img src="logo.png"/>, // a react component }, }, ... }); defining logo code snippet ... function MyComponent({Logo}) { return ( <div> <Logo/> </div> ... snip snip ); } export default withFassets({ component: MyComponent, mapFassetsToProps: { Logo: 'company.logo', } }); injecting fasset component properties HoC Higher-order Components
  21. 36.

    2. Feature Collaboration Feature Based UI Composition useFassets() export default

    createFeature({ name: 'common', fassets: { define: { 'company.logo': () => <img src="logo.png"/>, // a react component }, }, ... }); defining logo code snippet ... function MyComponent({Logo}) { return ( <div> <Logo/> </div> ... snip snip ); } export default withFassets({ component: MyComponent, mapFassetsToProps: { Logo: 'company.logo', } }); injecting fasset component properties export default function MyComponent() { const Logo = useFassets('company.logo'); return ( <div> <Logo/> </div> ... snip snip ); } useFassets() feature-u V2 supports React Hooks
  22. 37.

    • Supported by additional fasset directives • fassets.use: specify a

    series of injection needs • fassets.defineUse: supply this content • Validation Constraints can be specified • insuring everything is wired up correctly • Wildcards (*) provide additional dynamics • allowing content to be injected autonomously UI Composition can be a "contract" 2. Feature Collaboration Feature Based UI Composition Resource Contracts
  23. 38.

    2. Feature Collaboration Feature Based UI Composition Resource Contracts createFeature({

    name: 'main', fassets: { use: [ 'MainPage.*.link', ], }, }); src/features/main/feature.js code snippet ... export default function MainPage() { const mainLinks = useFassets('MainPage.*.link'); return ( <div> {/* links section */} {mainLinks.map( (MainLink, indx) => <MainLink key={indx}/>)} </div> ); } src/features/main/comp/MainPage.js createFeature({ name: 'cart', fassets: { defineUse: { 'MainPage.cart.link': () => <Link to="/cart">Cart</Link>, }, }, }); src/features/cart/feature.js createFeature({ name: 'search', fassets: { defineUse: { 'MainPage.search.link': () => <Link to="/search">Search</Link>, }, }, }); src/features/search/feature.js We have switched to a pull philosophy with autonomous injection!! 2. Feature Collaboration Feature Based UI Composition Resource Contracts
  24. 40.

    A/B Swap change a feature to a new version or

    swap out to a mock service or turn on a new feature in it's entirety
  25. 41.

    A/B Swap Services tailor a generalized solution into an app-specific

    domain greatly simplifying business logic Services promoted by features
  26. 42.

    A/B Swap service-based features through Usage Contracts Services are promoted

    through Usage Contracts • authService is a required fasset (use) • that conforms to an abstract AuthServiceAPI • two implementations are provided (defineUse): • Real/Mock • both implement AuthServiceAPI • enablement is mutually exclusive feature-u wires everything together
  27. 44.

    A final word about Feature-Based Development Features should mirror requirements

    Features are a different abstraction • because they contain other programming paradigms • features are more about a “logical packaging” so they plug-and-play What does this mean in terms of feature-u? Question: "How does feature-u impact my design constructs?" Answer: It doesn't! feature-u is NON Intrusive! • you employ same constructs and styles • you use same frameworks in the same way • only diff: your scope is smaller (i.e. a feature) feature-u frees you up to focus your attention on the "business end" of your features!
  28. 45.