Coder Social home page Coder Social logo

lajter's People

Contributors

petterik avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar

lajter's Issues

Redesign

Notes from textedit post clojure conj 2018 post hammock.

Has data requirements:

View

Cares about:

  • Fields to render.
  • Whether it's queryable or not.
    • Meaning: The way I'm querying this data, can I get it?
      • We can possibly check whether the query definition is possible against GraphQL API.

Does not (necessarily) care about:

  • Types of the fields
  • Parameters of where to get the fields
    • Routes or other paths in the app state structure.

Read

  • The definition of local reads can affect what's needed by the remote reads.

Local:

  • By specifying what's unique or what's required to satisfy a read, we can possibly declare a datamodel for reads, to avoid having to write reads.
  • Somehow, the specifications of the read need to be returned when asked for how to query it remotely.

Mutate

Local:

  • To perform an optimistic mutation:
    [RouteParams Env MutateParams] -> ?

Remote:

  • To perform a remote mutation, we don't really want to add params.
  • There is context involved with a mutation that should be inserted in to the
    remote query, such as RouteInfo (route and route-params), but otherwise
    the remote mutation should contain it all?
  • Is there anything that should be retrieved from these sources?
    • App state
    • URL
    • Local storage

Merge

Cares about:

  • Structured with a query:
    • ID's, how is this entity unique?
      • Single field
      • Multiple field
      • Singleton (enum/there's only one).
      • Component of cardinality 1, thus replace each time.
      • Component of cardinality many, collection cannot be normalized further.
  • DB transactions

Remote Query

Remote query is a merge of:

  • ViewQuery
    • (get-query RootView)
  • RemoteReadQuery
    • (remote-query ViewQuery)
  • MergeQuery
    • (ID specification of all queries).
    • Possibly from GraphQL API schema.
  • ContextParams
    • RouteInfo, and anything else specifying the current state of the client.

After merge, extract .

  • Such as GraphQL, EQL or REST(?)

Mechanical:

Local Query

  • ? Why did I write this section ?

Questions:

  • How are queries merged?

    • Do we merge queries by query root paths?
      • What about "_back-links", like in datomic.
        • Is that represented somehow in GraphQL?
  • Local Reads and Merge both care about ID specification

    • Can we do with:
      • View Query definitions
      • Query ID specifications
      • Read Query dependencies
        • Filters such as:
          • route data
          • "where clauses"?
      • Wait, Can we define RootQueries in hodur with meta data. See lacinia plugin.
        • Need to define examples of this.
        • The RootQueries defined in hodur can then also be merged with ViewQuery definitions
          • What does this mean ^. Is this awesome?
  • There's something about the property "full-name" that should be possible to do locally when there's a "first-name" and "last-name" in the GraphQL.

    • Implementing "pull" ourselves against DataScript given a query is probably a good thing. Using pathom for this is probably even better.
      • So, if someone has a query [Employees [full-name]]:
        • Remote query: [Employees [ID first-name last-name]]
          • Because full-name should expand to [first-name last-name].
          • Because ID is added to make them unique.
        • Local query resolution:
          1. 'Employees -> [Employee]
          2. (pull [full-name] [Employees])
          3. Pulling full-name will call a full-name resolver, which does (str first-name " " last-name).
        • Do we care? Maybe this resolution is up to GraphQL instead?
  • Regarding reverse lookup, that's a backend concern, and that's fine (it seems).

TODO:

  • Try to write an example app?
    • Wishful thinking.

What is the system? (model, state, logic)

Model

  • Domain Model

  • Root Queries

  • View Queries

    • Subscription Queries (special case of static queries).
  • Normal Model

    • Uniqueness/identity part of the domain model.
  • View data, such as:

    • Routes
    • Independent view children
    • Dependent view children (children who compose inside of the query)
    • Query
    • Subscriptions

Accumulated over time

  • RemoteLog
  • Layers
    • It's own thing.
      • Snapshotting a db from the layers could (in prod) remove remote layer data.

Logic:

  • Read(view-query, root-queries, app-state) -> render-data
  • Send(view-query, root-queries) -> remote-query
  • Merge(remote-response, remote-query, normalization-model, app-state) -> app-state
  • Reconcile(system) -> system
    • This part is purposely ambiguous.

State:

  • Route
  • LocalStorage
  • .. ?

TODO:

  • Given this definition of a system, I think we can redesign lajter. From the ground up? Maybe.
  • The parser concept might go away, not sure.
  • Definitely want to enable one to opt out of the Model definitions to configure everything.

Batch sending of layers

One use case:

When layers contain remote reads to the same remote (no mutations), they can be batched and sent as one request.

Support * in view queries

We can support * in view queries as we know all the fields of the model.

Good idea or bad? We stopped using * in sulolive, but if we were able to expand * to all the known fields in the entity, would we have kept on doing it?

Skip initial local parse when doing SSR

The initial root render & parsing is unnecessary when doing SSR.

The only thing you want is:

  • Remote send
  • Merge
  • Render

We did this with sulo by avoiding the code that called the other stuff.

Extend components (mixins?)

From clojurians #fulcro slack

(defsc MyComponent {:ident ... :query..}) ;; no render
(def MyComponentWeb (extend-component MyComponent {:life-cycle-methods ...})
(def MyComponentRN (extend-component MyComponent {:life-cycle-methods ...})

Test multiple update-state! calls between render

Call update-state! a bunch of times to see if the state gets set correctly.

Example:

(doto this
  (om/update-state! assoc :foo :bar)
  (om/update-state! assoc :abc :xyz))

Actually, do this with transact as well, to see if the props are correct.

Possibly declare UI's query as future clojure.spec data format

The big idea of om.next (imo) is that UI's declare their data requirements as data. Datomic's pull pattern syntax was a good way to achieve this. Depending on how clojure.spec decides to express spec's as data (which has been hinted now for a while), we may want to declare our queries in that format to get leverage from all the tools that are built upon that data format.

We could also check the clojure.spec registry whether a query key (root or field) is spec'ed and do validation on it.

Component macro

Not sure what to go for here.

People seem to like defsc from fulcro?

(defsc [this props]
  {:query ..
   ...
  }
  (dom/div nil "foo"))

I'm not so sure?

Removing layers (cancel sending of layers)

Use case:
Does:

  • User loads a page. Sends a huge request with everything on the page. Clicks on a different route.
    Expected:
  • To see the new route with data as soon as possible.
    Happens when we can only send one request at a time:
  • User will need to wait for the first request to finish + get merged into app state.

Solution(?):

  • Cancel the first request, dropping whatever would have been received. Send a new request with a query for the current route.

Clean up reconciler's p/to-env

The p/to-env implementation of the reconciler is pretty gross.

See if there's a better way to structure this code. Maybe put latest-db-from-layers! somewhere else?

Component and Suspendable lifecycles for the Reconciler

Make the reconciler stoppable to provide a path to remove any state and stuff.

Make the reconciler suspendable, to prevent renders, sends and stuff when code is reloading. When resuming the reconciler, it can clear caches that are no longer valid as code has been changed.

Remote responses

One of the things lost going from REST to om.next is remote mutation responses. Maybe even reads?

Where do we put them? We want to be app-state agnostic.

Into a layer? That's sweet?

Layers are also an answer to "is my request pending or not?".

update-state, transact without passing this

Could add new functions for interacting with a component that doesn't have to pass this

Example:

;; instead of:
(dom/button #js {:onClick #(la/update-state! this (fn [state] (assoc state :foo :bar)))})
;; Could have:
(dom/button #js {:onClick (la/update-state #(assoc % :foo :bar))})

Notice that:

  • update-state returns a function and that this is gone.
  • an anonymous function can be used to update the state, since the # char is not used to create an anonymous function.
  • the function has the bang ! removed. Not sure if that's good naming.

Same for transact, possibly for the computed functions as well and others?

Don't allow to render child which is not in :lajter/children

Children of a component must be declared in :lajter/children and calls to (render-child this child) should throw exceptions when called with a child that's not in the already defined children.

{:lajter/children []
 :render (fn [this props state] 
         (lajter.core/render-child this Child))}
                                     ;; ^^^^^^ is not in :lajter/children
;; This should throw

Layers

Add, replace, delete? layers.

Resolve layers during reconciliation?

Async layer application

Layer application can be quite harsh on the rendering. Experiment with making it async, only committing when all of the layers are applied.

Dynamic routes

Use case:

Code is loaded async and there may need to be new routes and new components (that aren't loaded yet) that has to be added to a components routing map on demand.

With sulo, we added a new root to the reconciler whenever a new module was loaded. Same approach here? Or something less "dramatic"?

Garbage collect layers in production

:layer.merge/value and :layer.snapshot/db can sometimes be garbage collected. It's not a big deal during development, but it reduces the memory foot print which is nice for production.

Cache the most recent app-state

We shouldn't have to re-calculate all the layers at every reconcile!. Use the most recent app-state as a base when the layers hasn't changed.

Incrementally update the app-state whenever layers are added.

;; Given
{:state {}}
;; Adds layers 1 2 3
{:layers [1 2 3]
 :state (db/with-layers {} [1 2 3])}
;; Now adding a new layer: 4, should calculate:
{:layers [1 2 3 4]
 :state (db/with-layers (db/with-layers {} [1 2 3]) [4])}
;; Where the first db/with-layers have been cached.

Add pull pattern to remote query reads

Use case:

;; Given query on a (visible) component:
{:query [{:read [:a :b :c]}]}
;; When transacted:
(la/transact! this '[(remote/foo) :read])
;; Remote query should be:
[(remote/foo) {:read [:a :b :c]}]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.