Skip to content
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

Support building reusable components / eco-systems #4265

Closed
vladsud opened this issue Nov 6, 2020 · 6 comments
Closed

Support building reusable components / eco-systems #4265

vladsud opened this issue Nov 6, 2020 · 6 comments
Assignees
Labels
area: runtime Runtime related issues design-required This issue requires design thought status: stale
Milestone

Comments

@vladsud
Copy link
Contributor

vladsud commented Nov 6, 2020

First, "components" below (for most part) means any piece of (reusable) code in container.
But occasionally I'll use more specific meaning - data store or data object (it should be obvious from context).

There are multiple motivations for the work that I'm proposing.
While they look different, they are all about one and the same thing:

  1. Today, it's almost impossible to build testable code. Software engineering as a science field built over time corpus of knowledge on how to build testable components / libraries, and it's hard to impossible to follow good practices in Fluid today:
  2. (Cont.) The Law of Demeter, or Principle of least knowledge: Each unit should have only limited knowledge about other units: only units "closely" related to the current unit. Each unit should only talk to its friends; don't talk to strangers. Only talk to your immediate friends. Anti-patterns that most of Fluid code unfortunately falls into are Service Locator, Context, Services, Registry, Handler
  3. In department of Building reusable code, we (collectively) also built over time key principles / patters to follow. The main one is Ports-and-adapters, also known as Hexagonal Architecture, It assumes adaptability, i.e. ability of some components in the system to adapt other components (build adapters, abstractions, etc.). I.e. direct control over inputs of other components.
  4. No or limited access to globals or singletons. There is a ton of material on internet on whys. CIontainerRuntimeBase exposed to every piece of code in container is that global - any component has a chance to impact behavior of any other component in the system, inspect whole system, or get access to any component in the system. This is goes agaist another key principle of designing systems - allow entities to have the least amount of privilege for it do it job.

All of these problems can be distilled really to the following missing functionality in framework (Aquaduct):

  1. When components (DataObjects) are created, they have to explicitly describe their dependencies, i.e. think of equivalence to constructor's arguments. Compiler should be able to validate that the caller (who instantiates DataObject) is providing all inputs, or fail compilation if right inputs are not provided (not delay failure to runtime!)
  2. Given that components do not control loading process (only creation), all such dependencies should be serializable and automatically (done by framework) be available after loading component in exactly same form as on creation path.
  3. All dependency / scope / etc. passing should be explicit, controlled by the caller, such that caller can adapt / create inputs for the callee. No access to ContainerRuntime.context.
  4. We should provide helper methods to ease the process of creation of serializable content, including wrapping non-serializable content. For example, something like Theming info can be provided by host for some components. instantiateRuntime() can provide serializable accessor object that exposes all or limited access to host environment, and pass that object only to components that need to have access to that data.
  5. Over (long) time, we should continue to remove access to globals and see how proper functionality can be provided to entities of the system on as needed bases, but not available by default.

Following this patterns will ensure components build using framework clearly describe all their dependencies, and can be reused by other (reusable) libraries by satisfying those requirements, either directly or though adaptation, or through propagating requirements up the stack to consumers of such library. It becomes possible to analyze systems/libraries in terms of their dependencies and behavious

@vladsud
Copy link
Contributor Author

vladsud commented Nov 6, 2020

@leeviana - FYI

@vladsud vladsud changed the title Support building reusable components / eco-system Support building reusable components / eco-systems Nov 6, 2020
@anthony-murphy
Copy link
Contributor

This is a SOLID proposal :)

@vladsud
Copy link
Contributor Author

vladsud commented Nov 6, 2020

Worth pointing out: Early (but more broader) thinking is described in this document

Certain topics might not be directly related to this issue, but it would be great to go over comments and see what other big area s are worth being extracted into their own top-level features.
For example, synchronous creation is likely worth being looked into.

@ghost ghost added the triage label Nov 6, 2020
@SamBroner SamBroner added area: runtime Runtime related issues design-required This issue requires design thought labels Nov 10, 2020
@DLehenbauer
Copy link
Contributor

DLehenbauer commented Nov 11, 2020

I know this pattern as the object-capability model, which is a security model where the parent process grants privileges to spawned child processes by passing a set of objects called 'capabilities' to the child at creation time.

Joe's article (above) links to lot of additional resources and the techniques mentioned all apply here (just replace language like "unforgeable reference to token" with "object like UndoManager".)

One twist are programs like a Window Manager or CLI that need the capability to launch arbitrary programs on the user's behalf, but do not themselves know anything about the program being launched or it's required capabilities.

In this situation, you generally end up with a powerful service at the top of the process tree that holds all of the user's capabilities and exposes weaker capabilities that allow programs like WM/CLI to enumerate and launch "pre-provisioned" programs. (One pattern is an installation service that persists user capabilities into the programs at install time.)

You might find that some of our customers are in a similar situation, with a general requirement to embed arbitrary components into an editing surface with limited knowledge of those components or their requirements. Moving such a customer to the capability model will likely require providing the equivalent of the installation/launcher service.

(Technically, containers are currently a closed system, so they could thread all capabilities through to every nested instance of every nested component that needs to create... but I think such a "ubiquitous-mega-capability-bundle" is at odds with a lot of the goals of this proposal.)

@ghost
Copy link

ghost commented Jul 8, 2021

This issue has been automatically marked as stale because it has had no activity for 180 days. It will be closed if no further activity occurs within 8 days of this comment. Thank you for your contributions to Fluid Framework!

@ghost ghost added the status: stale label Jan 5, 2022
@ghost
Copy link

ghost commented Jan 5, 2022

This issue has been automatically marked as stale because it has had no activity for 180 days. It will be closed if no further activity occurs within 8 days of this comment. Thank you for your contributions to Fluid Framework!

@ghost ghost closed this as completed Jan 13, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: runtime Runtime related issues design-required This issue requires design thought status: stale
Projects
None yet
Development

No branches or pull requests

5 participants