If you've tried stock Om Next, you probably found out pretty quickly that there are a lot of things you have to figure out, plug in, and set up before you actually have something working as a full-stack app with complete features. Fulcro is based on the model defined by Om Next, but makes a lot of the choices for you so that you can work on what you care about: your software!
The gist is that Fulcro is a kinder gentler version of Om Next.
(load this :people Person {:target [:person-list/by-id 3 :people]})
)Om Next has a suggested "default" client-side database format. Fulcro adopts this as the only choice for the client-side database. This has the following trade-offs:
get-in
, update-in
, or assoc-in
The primary disadvantage is...hm. We're not sure there is one! There are hooks that let you customize many of the things that are defined (e.g. you can hack into the client-side read parser).
Instead of parsers as the primary mechanism, Fulcro has you explicitly represent what you want from a query through the data model itself.
Om Next:
Query -> Parser -> Data Tree -> UI
|
reads (you implement)
Fulcro
Query -> db->tree -> UI
Thus, in Fulcro you essentially have the mutations update the data graph so that the query will be answered in the "correct" way.
While this sounds less powerful (it technically is), it turns out in practice that it makes general development much simpler. There is a hook that lets you supply a custom query handler in any situation in which you feel the need for it. This gives you the best of both worlds: choose to handle queries on the client when it suits you.
Adopting the model above leads to much easier development.
Om Next:
Fulcro:
Fulcro's remote story is separate, and not complected with the local data reads at all. Notice that in Fulcro your view updates are directly linked to an action of the UI (mutation) instead of logic that might have performance implications during rendering. The (sort of) down-side is that Fulcro is essentially asking you to always cache your calculation for rendering. This means your data model is slightly larger than what you might have in Om Next, but notice it also solves the potential performance bottleneck: you only re-calculate the data for a view when it changes, and those events are well-defined.
This is perhaps the very biggest positive result of Fulcro making the decisions it has made: elements of your program become much more isolated and modular!
Om Next
Unfortunately, your parser is usually tied to the UI structure you invent (you have to expect the UI to want to walk some particular graph). This means that if you want to pull out some "screen" and drop it in a devcard then you must customize the parser in order to support it, figure out what app state you need, compose it all together and hope that the devcard experience actually represents how it will behave within the real application. Since the remoting and processing of query roots is also tied to this concern it quickly becomes rather cumbersome and difficult. The same thing happens when two developers are working on the same application. You both end up working primarily on the parser, resulting in a higher likelihood of code conflicts on merge.
Fulcro
The predefined data model means there is no need for a custom parser. All components should have an ident, their initial state composes locally, and loads are not tied to global UI structure. If you want to put a screen in a devcard you just compose it into a new Root and drop it in place! The only thing that (typically) fails to work is navigation to UI stuff that you didn't bring along!
There is an older Untangled video on YouTube that gives you a taste for this in action.
Code conflicts become a lot less frequent as well since co-development is now almost completely isolated.
Om Next:
Fulcro:
In practice this is much much simpler and easier.
Both:
Om Next:
Fulcro:
Om Next defines the model of how to unify the local and remote interactions to the same data language (query + mutations) in order to make a nice data-driven application, but much of the implementation is up to you.
When doing networking: Om Next has you return remote information from your parser emitters. This joins the logic of network interaction with the local read processing. At first this sounds good: the logic for triggering the correct remote query is with the logic for locally satisfying it.
However, you must invent a lot here in order to get this all to work right:
process-roots
)Fulcro does all of this for you, and more. The big realization when designing Fulcro was this:
In Om Next, triggering loads happens under two circumstances:
The first has a clear point in time, and need not be related to parsing, and the second also has a clear point in time (i.e. a mutation happened due to a user action, timeout, etc).
Furthermore, you typically want to put some kind of "busy" indicator to keep track of what is loading (and render that), and then hack in some kind of state change code to put markers in app state to indicate when there is an error.
So, instead of having you write the logic of load into the places that are trying
to parse the query, Fulcro adds a higher-level load
function to address all of these.
Technically it is just a wrapper around a built-in mutation that adds a query
to a network processing queue (in app state) and triggers remote evaluation. You still use
UI-based queries, but you no longer have to worry about how it is tangled up in the UI (you get
to use a relative location rather than a query from the root).
A whiteboard discussion of this can be seen on YouTube.
Fulcro also defines more advanced merging behavior, auto-stripping of properties that the server should not see, sequential network processing (with parallel as an explicit option), websockets, error handling, load progress markers, etc.
In addition to the base client-side constructs Fulcro includes solutions for most of the common problems that business projects need: testing, i18n, tools for making full-stack forms, CSS, file uploads, debugging tools, support history viewer (unique to Fulcro at the time of this writing), core server operations, automatic serialization of remote requests, network activity indicators, automatic response merge conflict resolution, optional websocket integration, pre-constructed primitives for triggering loads along with data targeting, UI routers, and dynamic routers to support code splitting.
All of these are things you have to "plug in" with Om Next.
Fulcro doesn't lock you in to one solution for these (cljs uses Google's Closure compiler to eliminate unused code) It does, however, provide an included production-ready option that makes rapid application development possible.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close