LakeQL is organized into layers. Each layer depends only on the one below it, and the core contracts depend on nothing engine-specific.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Application layer
API / CLI / create-app / code generator
│
Composition layer
config / runtime / adapter registry
│
Adapter layer
adapter-trino / adapter-fabric / future adapters
│
Core contracts
datasource / query contracts
│
Infrastructure
storage / parquet / fabric-client / trino-client
Responsibilities #
-
Core contracts
(
@lakeql/datasource) define neutral interfaces for data sources, strategies, auth, and errors. They import nothing from Trino, Fabric, GraphQL, or Kysely. - Adapters ( Trino , Fabric ) own every engine-specific decision: connection, SQL dialect, strategies, and error mapping.
-
Composition
(
@lakeql/config) loads configuration, registers adapter factories, and composes a runtime — without knowing SQL or storage details. - Application ( api , cli ) consumes only the runtime and the core contracts. It does not import adapter packages for normal runtime decisions.
Why It's Layered This Way #
A new adapter provides its own connection, read, write, SQL, and response logic without changing any core package. Registration is explicit. You pass adapters into defineConfig. They are never discovered implicitly, and they never augment the config package through module augmentation. See Package Architecture for how the packages map onto these layers.