-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
[Epic] Decouple FE from the details of MBQL; write it in CLJC to unify logic with BE #28034
Comments
PSA: There's a distressing mix of identifier spelling in these underlying types. Broadly the JS objects are using The default return format for outgoing objects is |
Closing in favor of the sub-epics and sub-tasks of #28689 |
Background
Many parts of the Query Builder have code that cares about the gory details of MBQL queries. This is a major source of friction and complexity in the QB code, and a major source of small corner case bugs. Bugs get introduced when the FE's and BE's understanding of a query diverge, and when corner cases are missed in testing when a new feature is added (eg. forgetting to check multi-stage queries).
As an example of the details leaking out, some QB views check where a question is (a) based on a regular
source-table
, ie. a table in the warehouse; (b) based on another saved question, possibly a model; or (c) is a multi-stage nested query whose innermost query might be (a) or (b).Most of the code in
frontend/src/metabase-lib
deals with Metabase domain entities (Cards and Dashboards, Queries and Filters, Dimensions and Actions) and contains a lot of business logic around building, updating and querying them.Goals
Guidance
This code is likely to undergo several rounds of further refactoring in the future, so let's set ourselves up for success by making that easy to achieve.
Follow Clojure conventions for organizing namespaces; we are under no obligation to match the original folder structure. Also prefer putting code under
src/
, notshared/
-shared/
is considered deprecated.Eventual integration
The long-term plan is static TS functions which are mostly one-line wrappers for CLJC functions. The objects being passed around (
Card
,Table
, etc.) are CLJS maps treated as opaque by the FE code. This can be nicely enforced in TS with a bit of sleight of hand that creates distinct, "unforgeable", opaque types. (That is, TS can't see inside an opaqueQuery
, and if you use that opaqueQuery
in place of aCard
, you get a clear error.)We can enforce with the linter that only the
frontend/src/metabase-lib
code is allowed to import fromcljs/metabase.lib.*
. This will help future refactorings by giving us some indirection, and only one real call site to change.Transitional TS Classes
While we're making incremental ports, we'll be moving logic from the
Question
,StructuredQuery
etc. classes into CLJC functions. For now it's easy to make the methods simply wrap the CLJC functions. Eventually we'll refactor the call sites to just use the static functions inmetabase-lib/
and these classes can disappear.On Memoization
Some of the TS classes use a memoization helper with a pattern like this:
That's actually transparent to these ports, so no worries. (Unless you're removing one of the memoized methods. In that case just drop it from the list.)
On Testing
Write CLJC tests for the logic on the Clojure side.
Keep the JS/TS tests for the TS classes for things that still exist in TS. If you're deleting a helper function with TS tests, port the test cases into CLJC as well and delete the TS tests.
On CLJS and CLJC
Everything should be CLJC by default. We need a strong reason to write any separate CLJ and CLJS code here. The eventual goal is to have FE and BE both using the same logic for wrangling the Metabase domain entities like MBQL queries.
On Subclasses
A few of the entities (
{Native,Structured}Query
,*Dimension
) have nontrivial class hierarchies to them. We can preserve that during the transition, but eventually the division should disappear.Aim to write the CLJC functions as able to handle all the variations transparently, so the FE can call them without having to know or care about the differences. (Of course there are differences that matter - native queries and MBQL queries are handled differently in a few key places, like the native query editor! But lots of other places don't need to care about even that difference.)
Milestones
Milestone 0: Proof of concept
metabase.util
and friends to CLJC to unblock experimenting with Malli schemas in CLJCmetabase.util
to CLJC #28133defn
macro that combines wrapping/unwrapping with cljs-bean, and Malli schemas, for use in these ports.Milestone 0, part 2: Rebuild to opaque CLJS blob
Milestone 1: Port
LIMIT
LIMIT
on queries to use MLv2.Milestones 2-4: Port order-by, aggregations, and filters (probably in that order?)
Field
s, but expressions, aggregates from earlier stages, etc.) in a given stage.Milestones 5+: Keep chasing callers of legacy
metabase-lib/
functionality and porting themeg. many components pass around
Question
s, but once we make MLv2Card
s available in the Redux store, they should all be ported (in pieces) to use MLv2.There's a fair bit of this, but it's hard to map out and most of it can happen in any order.
The text was updated successfully, but these errors were encountered: