{"id":20489312,"url":"https://github.com/kevinast/feature-u","last_synced_at":"2025-04-13T16:32:41.902Z","repository":{"id":28483712,"uuid":"118521561","full_name":"KevinAst/feature-u","owner":"KevinAst","description":"Feature Based Project Organization for React","archived":false,"fork":false,"pushed_at":"2022-12-07T20:37:59.000Z","size":11175,"stargazers_count":91,"open_issues_count":15,"forks_count":6,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-04-26T01:06:15.140Z","etag":null,"topics":["expo","fdd","feature","feature-u","features","microfrontends","promoting-features","react","react-native","redux","redux-logic"],"latest_commit_sha":null,"homepage":"https://feature-u.js.org/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KevinAst.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-01-22T22:04:48.000Z","updated_at":"2024-04-22T00:39:09.000Z","dependencies_parsed_at":"2022-09-15T15:43:10.274Z","dependency_job_id":null,"html_url":"https://github.com/KevinAst/feature-u","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KevinAst%2Ffeature-u","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KevinAst%2Ffeature-u/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KevinAst%2Ffeature-u/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KevinAst%2Ffeature-u/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KevinAst","download_url":"https://codeload.github.com/KevinAst/feature-u/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248744015,"owners_count":21154790,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["expo","fdd","feature","feature-u","features","microfrontends","promoting-features","react","react-native","redux","redux-logic"],"created_at":"2024-11-15T17:12:19.300Z","updated_at":"2025-04-13T16:32:41.877Z","avatar_url":"https://github.com/KevinAst.png","language":"JavaScript","readme":"# feature-u\n\n**feature-u** is a utility library that facilitates **Feature-Driven\nDevelopment** in your [`react`] project.  It provides tangible\nassistance in promoting features that are truly **plug-and-play**.\n\nYou can quickly **\"come up to speed\"** with **feature-u** by viewing\nthe [`Playful Features Video`], that builds concepts, and demonstrates\nthem in a real world app ([`eatery-nod-w`]).\n\n\u003c!--- Badges for CI Builds ---\u003e \n[![Build Status](https://travis-ci.org/KevinAst/feature-u.svg?branch=master)](https://travis-ci.org/KevinAst/feature-u)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c063a6e2859148e8baa89e9369b0fa5d)](https://www.codacy.com/app/KevinAst/feature-u?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=KevinAst/feature-u\u0026amp;utm_campaign=Badge_Grade)\n[![Codacy Badge](https://api.codacy.com/project/badge/Coverage/c063a6e2859148e8baa89e9369b0fa5d)](https://www.codacy.com/app/KevinAst/feature-u?utm_source=github.com\u0026utm_medium=referral\u0026utm_content=KevinAst/feature-u\u0026utm_campaign=Badge_Coverage)\n[![Known Vulnerabilities](https://snyk.io/test/github/kevinast/feature-u/badge.svg?targetFile=package.json)](https://snyk.io/test/github/kevinast/feature-u?targetFile=package.json)\n[![NPM Version Badge](https://img.shields.io/npm/v/feature-u.svg)](https://www.npmjs.com/package/feature-u)\n\n## Introduction\n\n**Feature-Driven Development** (**FDD**) has become more prevalent in\ntoday's landscape, and for good reason!  This is a lightweight Agile\ntechnique, manifest in a project structure where your code is\norganized by what it accomplishes (i.e. features), rather than lumping\nall modules of like types into separate blobs of components, routes,\nlogic, actions, etc.  This technique greatly improves your code\ncomprehension because there is a direct correlation between the\n**problem space** _(the requirements)_ and the **implementation**\n_(the code)_!\n\nMost developers would agree that organizing your project by feature is\nmuch preferred over type-based patterns.  Because **application\ndomains grow** in the real world, project **organization by type\nsimply doesn't scale**, _it just becomes unmanageable_!\n\nHowever, **FDD** involves more than just organizing your project's\ndirectory structure into features. You want to encapsulate your\nfeatures into isolated and self-sufficient modules, and yet they must\nalso be able to collaborate with other features.\n\nTruly isolated **FDD** is something that is **incredibly powerful**!\nYou can improve the modularity of your system by loosely coupling your\nfeatures, making your app easier to understand, develop, test, and\nrefactor.  If done right, your features actually become **\"miniature\napplications\"** that simply **plug-and-play** _(where the mere\nexistence of a feature dynamically exudes the characteristics it\nimplements)_!\n  \nAs it turns out there are a number of hurdles to overcome in order to\naccomplish this. Rather than being left to fend for yourself,\n**feature-u** has already tackled these hurdles.\n\n**feature-u** promotes a new and unique approach to **code\norganization** and **app orchestration**.\n\nWith **feature-u** ...\n\n- your features can be encapsulated and isolated\n\n- they can collaborate with other features in an extendable way\n\n- your components can employ cross-feature composition (even injecting\n  their content autonomously)\n\n- your features can initialize themselves\n\n- they can be activated or deactivated at run-time\n\n- and as a bonus, your frameworks will even auto-configure with only\n  the active features _(via a plugin architecture)_\n\n**feature-u** opens new doors into the exciting world of **FDD**. It\nfrees you up to focus your attention on the \"business end\" of your\nfeatures!\n\n\n## Install\n\n- **peerDependencies**:\n\n  **feature-u** has a peerDependency on react _(most likely you should\n  already have this installed ... but just in case)_:\n\n  ```shell\n  npm install --save react\n  ```\n  \u003c!--- WITH REVEAL of USAGE:\n  npm install --save react   # VER: \u003e=0.14.0   USAGE: React Context and JSX (in withFassets.js and launchApp.js)\n  ---\u003e \n\n- **the main event**:\n\n  ```shell\n  npm install --save feature-u\n  ```\n\n## feature-u Basics\n\nThe basic process of **feature-u** is that each feature promotes a\n[`Feature`] object that contains various aspects of that\nfeature ... _things like: the feature's name, it's Public Interface,\nwhether it is enabled, initialization constructs, and resources used\nto configure it's slice of the frameworks in use._\n\nIn turn, these [`Feature`] objects are supplied to [`launchApp()`],\nwhich configures and starts your application, returning a [`Fassets`] object\n(_which promotes the Public Face of each feature_).\n\n![Basic Concepts](docs/img/concepts.png)\n\nIn **feature-u**, \"aspect\" is a generalized term used to refer to the\nvarious ingredients that (when combined) constitute your application.\nAspects can take on many different forms: **UI Components** \u0026bull; **Routes**\n\u0026bull; **State Management** _(actions, reducers, selectors)_ \u0026bull;\n**Business Logic** \u0026bull; **Startup Initialization Code** \u0026bull;\n_etc. etc. etc._\n\n**Not all aspects are of interest to feature-u** ...  _only those that\nare needed to setup and launch the app_ ... all others are considered\nan internal implementation detail of the feature.  As an example,\nconsider the redux state manager: while it uses actions, reducers, and\nselectors ... only reducers are needed to setup and configure redux.\n\nA fundamental goal of **feature-u** is to **automatically configure\nthe framework(s)** used in your run-time-stack _(by accumulating the\nnecessary resources across all your features)_.  Because not everyone\nuses the same frameworks, **feature-u** accomplishes this through\n**Extendable Aspects** _(you can find them in external NPM packages,\nor you can create your own)_.  The interface to your chosen frameworks\nis not altered in any way.  You use them the same way you always have\n_(just within your feature boundary)_.\n\n## Usage\n\nThe basic usage pattern of **feature-u** is to:\n\n1. Organize your app into features.\n\n   * Each feature should be located in it's own directory, typically\n     within a `features/` parent directory.\n\n   * How you break your app up into features will take some time and\n     thought.  There are many ways to approach this from a design\n     perspective.\n\n   * Each feature will promote it's characteristics through a\n     [`Feature`] object (using [`createFeature()`]).\n\n   * A `features/index.js` module will accumulate and promote all of\n     the [`Features`] that make up your entire application.\n\n1. Choose the [`Aspects`] that you will need, based on your\n   selected frameworks (i.e. your run-time stack).\n\n   * Typically these [`Aspects`] are packaged separately in\n     NPM, although you can create your own (if needed).\n\n   * Each [`Aspect`] will extend the properties accepted by the\n     Feature object (for example: `Feature.reducer` for [`redux`], or\n     `Feature.logic` for [`redux-logic`]).\n\n   * A best practice is to organize an `aspects/` directory, mimicking\n     the same pattern as your `features/` directory.\n\n   * An `aspects/index.js` module will accumulate and promote all of\n     the aspects used by your application.\n\n1. Your mainline will start the app by invoking [`launchApp()`], passing\n   all [`Features`] and [`Aspects`].\n\n**Easy Peasy!!**\n\n\n## Directory Structure\n\nHere is a sample directory structure of an app that uses **feature-u**:\n\n```\nsrc/\n  app.js              ... launches app using launchApp()\n\n  aspects/\n    index.js          ... accumulate/promote all Aspect objects (used by the app)\n\n                      ... NOTE: the aspects/ dir can contain local Aspects, however\n                                because most Aspects are pulled from external \n                                NPM packages, this directory is typically empty!\n\n  features/\n    index.js          ... accumulate/promote all Feature objects (for the entire app)\n\n    featureA/         ... a feature (within the app)\n      actions.js\n      appDidStart.js\n      appWillStart.js\n      comp/\n        ScreenA1.js\n        ScreenA2.js\n      feature.js      ... promotes featureA object using createFeature()\n      index.js        ... redirect parent dir import to the feature object\n      logic.js\n      reducer.js\n      route.js\n\n    featureB/         ... another feature\n      ...\n\n  util/               ... common utilities used across all features\n    ...\n```\n\nEach feature is located in it's own directory, containing it's aspects\n(actions, reducers, components, routes, logic, etc.).\n\n\n## Feature Object\n\nEach feature promotes it's aspect content through a\n[`Feature`] object (using [`createFeature()`]).\n\n**`src/feature/featureA/index.js`**\n```js\nimport {createFeature}  from 'feature-u';\nimport reducer          from './state';\nimport logic            from './logic';\nimport route            from './route';\nimport appWillStart     from './appWillStart';\nimport appDidStart      from './appDidStart';\n\nexport default createFeature({\n  name:     'featureA',\n  enabled:  true,\n\n  fassets: {\n    define: {\n      'api.openA':  () =\u003e ... implementation omitted,\n      'api.closeA': () =\u003e ... implementation omitted,\n    },\n  },\n\n  reducer,\n  logic,\n  route,\n\n  appWillStart,\n  appDidStart,\n});\n```\n\nThe docs will fill in more detail, but for now notice that the feature\nis conveying reducers, logic modules, routes, and does some type of\ninitialization (appWillStart/appDidStart).  It also promotes something\ncalled `fassets` (feature assets - the Public Face of a feature) with\n`openA()` and `closeA()` functions which will be publicly promoted to\nother features.\n\n**Note**: Feature directory imports are redirected to the feature\nobject reference ... for example:\n\n**src/features/featureA/index.js**\n```js\n// redirect parent dir import to the feature reference\nexport {default} from './feature';\n```\n\n\u003c!--- START COMMENT: Feature/Aspect Accumulation is too much for README\n\n## Feature Accumulation\n\nAll [`Features`] are accumulated in a single `index.js`\nmodule, allowing them to be promoted through a single import.\n\n**src/features/index.js**\n```js\nimport featureA  from './featureA';\nimport featureB  from './featureB';\n\n// promote ALL app features through a single import (accumulated in an array)\nexport default [\n  featureA,\n  featureB,\n];\n```\n\n**Note**: While this represents a complete list of all app features,\nsome of them may be disabled (i.e. logically removed) ... see:\n[`Feature Enablement`].\n\n\n## Aspect Accumulation\n\nA best practice is to accumulate all [`Aspects`] in a single\n`aspects/index.js` module, allowing them to be promoted through a\nsingle import.\n\n**src/aspects/index.js**\n```js\nimport React                  from 'react';\nimport {createReducerAspect}  from 'feature-redux';\nimport {createLogicAspect}    from 'feature-redux-logic';\nimport {createRouteAspect}    from 'feature-router';\nimport SplashScreen           from 'util/SplashScreen';\n\n// define/configure the aspects representing the app's run-time stack\n// ... redux - extending: Feature.reducer\nconst reducerAspect = createReducerAspect();\n// ... redux-logic - extending: Feature.logic\nconst logicAspect   = createLogicAspect();\n// ... Feature Routes - extending: Feature.route\nconst routeAspect   = createRouteAspect();\n// ... CONFIG: define fallback screen (used when no routes are in effect)\nrouteAspect.config.fallbackElm$ = \u003cSplashScreen msg=\"I'm trying to think but it hurts!\"/\u003e;\n\n// promote the aspects representing the app's run-time stack\nexport default [\n  reducerAspect,\n  logicAspect,\n  routeAspect,\n];\n```\n\nThese [`Aspects`] _(pulled from external npm packages)_\nreflect the frameworks of the app's run-time stack _(in this example\n[`redux`], [`redux-logic`], and\n[`feature-router`])_ and extend the acceptable Feature\nproperties _(`Feature.reducer`, `Feature.logic`, and `Feature.route`\nrespectively)_ ... _**see:** [`Extendable aspects`]_\n\n**Note**: The main difference in this module (vs. `features/index.js`)\nis that it is typically pulling/configuring resources from external\nNPM packages, rather than locally defined within the project\n_(although you can create your own if needed)_.\n\nEND COMMENT ---\u003e \n\n## launchApp()\n\nIn **feature-u** the application mainline is very simple and generic.\nThere is no real app-specific code in it ... **not even any global\ninitialization**!  That is because **each feature can inject their own\napp-specific constructs**!!  The mainline merely accumulates the\n[`Features`] and [`Aspects`], and starts the app by\ninvoking [`launchApp()`]:\n\n**src/app.js**\n```js\nimport ReactDOM     from 'react-dom';\nimport {launchApp}  from 'feature-u';\nimport features     from 'features';\nimport aspects      from 'aspects';\n\n// launch our app, exposing the Fassets object (facilitating cross-feature-communication)\nexport default launchApp({          // *4*\n                                    \n  features,                         // *1*\n  aspects,                          // *2*\n\n  registerRootAppElm(rootAppElm) {  // *3*\n    ReactDOM.render(rootAppElm,\n                    document.getElementById('root'));\n  },\n});\n```\n\nHere are some **important points of interest** _(match the numbers to\n`*n*` in the code above)_:\n\n1. all app features are supplied (accumulated from the `features/`\n   directory) ... _**see:** [`Feature Accumulation`]_\n\n2. the app aspects (i.e. the run-time stack) are supplied (accumulated\n   from the `aspects/` directory) ... _**see:** [`Aspect Accumulation`]_\n\n3. a [`registerRootAppElm()`] callback is used to catalog the\n   supplied `rootAppElm` to the specific React platform in use.\n   Because this registration is accomplished by your app-specific\n   code, **feature-u** can operate in any of the React platforms, such\n   as: React Web, React Native, Expo, etc. ... _**see:**\n   [`React Registration`]_\n\n4. _as a bit of a preview_, the return value of [`launchApp()`] is a\n   [`Fassets`] object, which promotes the accumulated Public Face of\n   all features, and is exported to provide [`Cross Feature\n   Communication`] ... _here is what the `fassets` looks like (for\n   this example):_\n\n   ```js\n   fassets: {\n     api: {\n       openA(),\n       closeA(),\n     },\n   }\n   ```\n\nHopefully this gives you a basic feel of how **feature-u** operates.\n\n\n## Comprehensive Documentation\n\nThe sample above just scratches the service!\n\n**Comprehensive Documentation** can be found at https://feature-u.js.org/,\nwhich includes both a **Dev Guide** *(building concepts with full and\nthorough **examples**)*, and a complete **API Reference**.\n\n\n## Benefits\n\nThe benefits of using **feature-u** include:\n\n1. **Feature Encapsulation:**\n   _isolating feature boundaries improves code manageability_\n\n1. **Feature Collaboration:**\n   _promote **Cross Feature Communication** through a well-defined\n   feature-based Public Interface_\n\n1. **Feature Based UI Composition:**\n   _facilitate seamless **cross-feature component composition**_\n\n1. **Application Life Cycle Hooks:**\n   _features can initialize themselves without relying on an external\n   process_\n\n1. **Feature Enablement:**\n    _enable/disable features through a run-time switch_\n\n1. **Minimize Feature Order Dependency Issues**\n   _during in-line code expansion_\n\n1. **Framework Integration:**\n   _automatically configure used framework(s) (matching the app's\n   run-time-stack) by accumulating all feature aspects (employing an\n   extendable API)_\n\n1. **UI Component Promotion:**\n   _features can autonomously promote their UI components through\n   Feature Based Route Management_\n\n1. **Single Source of Truth:**\n   _is facilitated in a number of ways within a feature's\n   implementation_\n\n1. **Simplified App Startup:**\n   _launching an app can be accomplished through a single line of\n   executable code!_\n\n1. **Operates in any React Platform**\n   _React Web, React Native, Expo, etc._\n\n1. **Plug-and-Play:**\n   _features can be more easily added or removed_\n\n\n## Real Example\n\nWant to see a real **feature-u** app?\n\n[`eatery-nod-w`] is the application _where **feature-u** was\nconceived_.  It is a [`react-native`] [`expo`] mobile\napp, and is one of my sandbox applications that I use to test\nframeworks.  _I like to develop apps that I can use, but have enough\nreal-world requirements to make it interesting._\n\n**[`eatery-nod-w`]** randomly selects a \"date night\" restaurant\nfrom a pool of favorites.  _My wife and I have a steady \"date night\",\nand we are always indecisive on which of our favorite restaurants to\nfrequent :-)_ So **[`eatery-nod-w`]** provides the spinning\nwheel!\n\n\n## Video Presentation\n\nWhen grasping a new concept, it is helpful to **see it in action!**\n\nYou can quickly **\"come up to speed\"** with **feature-u** by reviewing\nthe [`Playful Features Video`].\n\nThis is a **screencast video** of a presentation that has been given\nto a number of regional conferences and local meetup groups.  It\nclosely follows the [`Basic Concepts`] section, and demonstrates the\nnewly developed concepts in a real world app ([`eatery-nod-w`]).\n\n\nI hope you enjoy **feature-u**, and comments are always welcome.\n\n\u0026lt;/Kevin\u0026gt;\n\n\n[`Fassets`]:                      https://feature-u.js.org/cur/api.html#Fassets\n[`Feature`]:                      https://feature-u.js.org/cur/api.html#Feature\n[`Features`]:                     https://feature-u.js.org/cur/api.html#Feature\n[`Aspects`]:                      https://feature-u.js.org/cur/api.html#Aspect\n[`createFeature()`]:              https://feature-u.js.org/cur/api.html#createFeature\n[`launchApp()`]:                  https://feature-u.js.org/cur/api.html#launchApp\n[`registerRootAppElm()`]:         https://feature-u.js.org/cur/api.html#registerRootAppElmCB\n[`eatery-nod-w`]:                 https://github.com/KevinAst/eatery-nod-w\n[`expo`]:                         https://expo.io/\n[`feature-router`]:               https://github.com/KevinAst/feature-router\n[`react-native`]:                 https://facebook.github.io/react-native/\n[`react`]:                        https://reactjs.org/\n[`redux-logic`]:                  https://github.com/jeffbski/redux-logic\n[`redux`]:                        http://redux.js.org/\n[`Cross Feature Communication`]:  https://feature-u.js.org/cur/crossCommunication.html\n[`Extendable aspects`]:           https://feature-u.js.org/cur/detail.html#extendable-aspects\n[`React Registration`]:           https://feature-u.js.org/cur/detail.html#react-registration\n[`Feature Enablement`]:           https://feature-u.js.org/cur/enablement.html\n[`Feature Accumulation`]:         https://feature-u.js.org/cur/usage.html#feature-accumulation\n[`Aspect Accumulation`]:          https://feature-u.js.org/cur/usage.html#aspect-accumulation\n[`Playful Features Video`]:       https://feature-u.js.org/cur/presentation.html\n[`Basic Concepts`]:               https://feature-u.js.org/cur/concepts.html\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkevinast%2Ffeature-u","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkevinast%2Ffeature-u","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkevinast%2Ffeature-u/lists"}