-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature-u Enhancement: Rebase Active Features at run-time #27
Comments
@sylvainlg ... you may have interest in this ... comments are welcome :-) |
Wow this is a really interesting RFC. Uses :
I have some questions :
|
Some of my co-workers asked me about a related subject. |
Thanks for your questions @sylvainlg. Regarding question: "How aspects react to a rebase" ... A: they would not (i.e. nothing would change). Aspect interaction only occurs during the startup process (via Regarding question: "How define/use contract react to a rebase?" ... A: the same way they always have (i.e. nothing would change). A Regarding questin: "How redux and react-navigation would take the adds and removes of there assets?" ... A: Because Regarding complexity, my goal would to make
I think this can be accomplished if your features are properly designed ... loosely coupled, using autonomous injection ... a pull philosophy that makes plug-and-play possible. For those who are not familiar with this, it is discussed in Resource Contracts and is shown in the Video Presentation at: I would encourage anyone to view the video in it's entirety :-) Hope this helps! Thoughts? P.S. Regarding React.lazy and Suspense, I have no experience with this (sorry). I would tend to think it should work, but you may want to try a simple test. FYI: I have no immediate plans to implement this design (I am very busy on another project). At this point, I just wanted to get the design out for feedback. |
Really interested in this one too. Currently what I'm doing is pretty straight forward. I have an app with some additional services that can be purchased. The module is active all time and using a fasset and a feature module "firebase remote config" i can say whether the feature is active or not using the fassets. |
RFC (Request for Comments)
This design is an initial draft of a feature-u enhancement that will:
Allow the set of active features to change after the app is fully "up and running"!!!
This design has been published here in order to solicit feedback from you. Please leave your insight in the comments section below. Your help in improving feature-u is greatly appreciated!
At a Glance
The Need
One of the most powerful aspects of feature-u is it's ability to dynamically determine the set of active features (via Feature Enablement). This, in conjunction with Cross Feature Collaboration, enables true plug-and-play capabilities ... where the mere existence of a feature exudes the characteristics it implements.
While this is indeed a very powerful capability, it currently has a limitation ... that is the determination of "active features" is accomplished only once throughout the entire life cycle of the app ... at startup time (via
launchApp()
).We need an ability to change the active features at run-time.
As an example of this, consider that different users should be able to manifest a distinct feature set, based on their enablement. A "guest" user will operate under a "bare minimum" feature set, while a "licensed" user will benefit from more advanced features.
The problem in this scenario is that the user in question is determined after the app has started ... once authentication has completed.
There needs to be a way to "rebase" the active features after the app has started (i.e. at run-time)!
The Issue
In thinking about this, a broad solution of "rebasing" features can be difficult. The issue is that the initial set of active features (from
launchApp()
) determines very low-level characteristics ... such as framework configuration.Take redux state management as an example. Once the app is up and running, it would be difficult to introduce additional state from the activation of a new feature (one that was not already configured at startup).
Presumably, this would be an issue for other frameworks as well.
There is a lot of to consider here, and I am fearful of introducing needless complexity. From a broad perspective, if we wanted to rebase every characteristic of a feature, I am not completely sure how this could be reliably accomplished.
The Solution (potential)
In my experience, a "rebasing" of active features that only recalculates
fassets
would go a long way to solving this problem, and could be accomplished fairly easily.Background:
fassets
are the public API of features, and are the mechanism by which Cross Feature Collaboration is accomplished in an extendable way.Caveat:
The caveat here is that any newly activated feature (resulting from the "rebase" operation) must either:
fasset
definitionslaunchApp()
).Any other scenario would error out, because we are not supporting the rebase of things like frameworks.
Example:
As an example, if an "advanced" feature was exposed by promoting it's menu item through autonomous injection (a
fassets
Resource Contract), then when that feature was activated/deactivated, it would dynamically appear/disappear.I have run across this exact scenario in my feature-based applications.
Mitigated Limitations:
I can conceive of scenarios where this limited "rebase" approach could have the potential of being problematic.
With that being said, I have not seen this in practice (I believe it to be a low probability). My current thought is that if these scenarios do in fact occur:
it may represent a more encompassing application design issue (say an inappropriate coupling)
even if this is not the case, there are work-arounds (for example, conditional logic based on whether self's feature is active or not)
Consider redux-logic, where logic modules monitor redux actions. If our "rebase" operation deactivated a feature that contained logic, then technically that logic would still be active. This is because logic is "bound" to it's monitored actions very early, at start-up time (i.e.
launchApp()
). In the case of monitoring external actions (from other features) this is accomplished through Cross Feature Collaboration usingexpandWithFssets()
(see Managed Code Expansion).GREAT: normally, the monitored actions are internal to a feature, and so the actions would never be emitted/consumed.
However, consider external actions that are monitored by this deactivated feature.
GREAT: inactive external features would never emit actions that are being monitored, so "no harm no foul".
PROBLEM: however if active external features emit actions that are being monitored, then the logic from our "inactive" feature would incorrectly execute. The ramification of this would vary and ultimately be application specific. As mentioned above, this can be mitigated with additional application logic.
I know this is an "involved" topic, but any thoughts?
The Proposal
I propose the introduction of a
rebase()
function, which can be invoked anytime during the application life cycle. This function will re-analyze the set of active features, and result in an updatedfassets
object, reflecting thefassets
promoted from the current active set of features.With this enhancement, the
fassets
object can now actually change for the first time. As a result, there are some minor tweaks in how it is accessed.API
Here is "rough cut" of the proposed API (some new, some changed):
Feature.enabled
The Feature
enabled
flag (see Feature Enablement) can now either resolve to a boolean (as always) or be anenablementFn
(new).enablementFn
The
enablementFn
is used when additional context is needed in the determination of the active feature set. The function is invoked by feature-u, and has the following signature:Params:
isStartup
: a boolean indicating if the app is being started. Whentrue
we know thatlaunchApp()
is in control (one time only). Whenfalse
thenrebase()
is driving the process (can be many times).enablementCtx
: additional enablement context supplied by the application throughlaunchApp()
andrebase()
parameters.launchApp()
The
launchApp()
function has a slightly refined signature:Params:
enablementCtx
: a new optional parameter that provides the enablement context to theenablementFn()
. As an example, this could be the authenticated User object.Returns:
FeatureCtx
object (see below). PreviouslylaunchApp()
returned afassets
object.FeatureCtx
The
FeatureCtx
object is returned fromlaunchApp()
.For the most part, this is an opaque object, holding internal information about the app's feature set, and is used to seed the new
rebase()
function.It does have some public API:
getFassets(): fassets
returns the currentfassets
object (the original return fromlaunchApp()
)hasFeature(featureName): boolean
: determines if a feature is present or not (originally part of thefassets
object)rebase()
A new
rebase()
function is introduced:This function can be invoked as needed any time after
launchApp()
has completed, and will "rebase" the set of active features, defined from the suppliedfeatureCtx
(obtained fromlaunchApp()
).Params:
featureCtx
: theFeatureCtx
object (discussed above), obtained fromlaunchApp()
, which identifies the overall set of app features.enablementCtx
: the optional parameter that provides the enablement context to theenablementFn()
. As an example, this could be the authenticated User object.The result of this invocation will be a new rendition of the
fassets
object (found infeatureCtx.getFassets()
), which is used in all cross feature collaboration.Not only will a new
fassets
be generated, but because this is a "React Context Object", the application display will dynamically refresh, reflecting the latest rendition offassets
, as determined by the new active feature set.Nice!!
Example Usage
This is a modified example, taken from eatery-nod-w:
The discovery feature is only available to licensed users. However it must be initially activated in order to configure it's framework pieces:
feature.js
The eateries feature is always active, so it has no
enabled
directive (as always):feature.js
The eateryServiceMock feature only contains
fassets
definitions, so it can be activated/deactivated at any time, regardless of whether it was initially active at start-up. In other words it is not concerned with theisStartup
directive.feature.js
The sandbox feature's enablement is controlled through an independent run-time expression:
feature.js
The
launchApp()
invocation:exports the
featureCtx
for subsequent usedoes not supply the
enablementCtx
because there is no authenticateduser
at startup timeapp.js
Here is a
rebase()
invocation:it can be invoked many times
it pulls in the
featureCtx
from the return oflaunchApp()
(in app.js).it supplies the
enablementCtx
because there is an authenticateduser
at this time.The text was updated successfully, but these errors were encountered: