How to Use — Recommended LookML Project Structure¶
Droughty generates your base LookML layer automatically, but it generates into a larger project that you own and extend. This guide describes the folder structure used by production Looker projects built on top of droughty, explains what belongs where, and shows how the generated files slot in.
Note
This is the structure used on multi-schema dbt + BigQuery or Snowflake projects. The folder names are conventions, not requirements — Looker only cares about include: paths. But consistent naming makes projects far easier to navigate.
Full folder structure¶
lookml/
├── models/
│ └── my_project.model.lkml # Model file — declares explores and global includes
│
├── base/ # ← droughty writes here — DO NOT edit these files
│ ├── _base.layer.lkml # Base views generated from warehouse schema
│ ├── _aggregate.layer.lkml # Base views for aggregate tables
│ └── _base_parameters.layer.lkml # PoP parameters layer (if lookml_pop is configured)
│
├── staging/ # Refinements of base views — light customisations
│ ├── stg_commerce.layer.lkml
│ ├── stg_finance.layer.lkml
│ ├── stg_core.layer.lkml
│ └── stg_marketing.layer.lkml
│
├── int/ # Integration layer — explores, joins, business logic
│ ├── int_explore_commerce.explore.lkml
│ ├── int_explore_finance.explore.lkml
│ ├── int_explore_core.explore.lkml
│ └── int_explore_marketing.explore.lkml
│
├── aggregate/ # Aggregate-awareness layers
│ ├── agg_commerce.layer.lkml
│ ├── agg_finance.layer.lkml
│ ├── agg_core.layer.lkml
│ └── agg_marketing.layer.lkml
│
├── derived/ # Derived tables (PDTs)
│
├── utils/ # Shared utility dimensions and measures
│ └── utils_period_boundaries.layer.lkml
│
└── dashboards/ # LookML dashboard definitions
├── commerce__orders_overview.dashboard.lookml
├── commerce__customers_retention.dashboard.lookml
└── ...
What goes in each folder¶
base/¶
Auto-generated by droughty. Never edit these files.
This folder is the contract between your warehouse schema and Looker. Every table in your analytics schema gets a view here, and every column gets a dimension. When the schema changes, you run droughty lookml and these files are overwritten.
Files droughty writes here:
File |
Source |
|---|---|
|
|
|
|
|
|
Because these are .layer.lkml files (using Looker refinements), they extend the base view definitions rather than replacing them.
staging/¶
You own these files. Refinements of the base views — the right place for:
Hiding or relabelling dimensions that don’t belong in Looker
Overriding
sql:for computed fieldsAdding
drill_fieldssetsSetting field-level access filters
Adding
tagsfor content organisation
File naming convention: stg_{domain}.layer.lkml — one file per domain or functional area. Each file uses view: +table_name { } blocks (refinements) so its changes stack on top of the base layer without touching it.
Example (stg_commerce.layer.lkml):
view: +fct_orders {
dimension: order_pk {
hidden: yes # already hidden by droughty but confirmed explicitly here
}
dimension: order_status {
label: "Status"
tags: ["commerce", "orders"]
}
measure: total_revenue {
type: sum
sql: ${order_value} ;;
value_format_name: gbp
}
}
int/¶
You own these files. The integration (explore) layer — where you define explores and their joins. This is what Looker users actually see when they open an explore.
Use .explore.lkml for files that contain explore: blocks, and .layer.lkml for files that only add refinements to explores.
File naming convention: int_explore_{domain}.explore.lkml
Example (int_explore_commerce.explore.lkml):
explore: fct_orders {
label: "Orders"
join: dim_customers {
type: left_outer
sql_on: ${fct_orders.customer_fk} = ${dim_customers.customer_pk} ;;
relationship: many_to_one
}
join: dim_products {
type: left_outer
sql_on: ${fct_orders.product_fk} = ${dim_products.product_pk} ;;
relationship: many_to_one
}
}
aggregate/¶
You own these files. Aggregate awareness layers — Looker-level rollup tables that let it query pre-aggregated data instead of scanning the full fact table when possible.
Only needed if you have aggregate tables in your warehouse (e.g. daily/monthly rollup tables built in dbt). The file points Looker at the aggregate view and maps its fields to those in the fact explore.
utils/¶
You own these files. Shared utility dimensions and measures that are reused across multiple views or explores. Common examples:
Period boundary dimensions (
utils_period_boundaries.layer.lkml)Shared fiscal calendar logic
Currency conversion dimensions
dashboards/¶
You own these files. LookML dashboards — version-controlled dashboard definitions. These allow dashboards to be deployed with the LookML project rather than created ad-hoc in the Looker UI.
derived/¶
You own these files. Persistent Derived Tables (PDTs) — SQL or LookML-defined derived tables that Looker materialises in the warehouse.
models/¶
You own this file. The model file ties the project together. It declares which explores are available in the project and which files to include:
Example (my_project.model.lkml):
connection: "my_bigquery_connection"
# Base layer (generated by droughty)
include: "/lookml/base/_base.layer.lkml"
include: "/lookml/base/_aggregate.layer.lkml"
include: "/lookml/base/_base_parameters.layer.lkml"
# Staging refinements
include: "/lookml/staging/*.layer.lkml"
# Utility layers
include: "/lookml/utils/*.layer.lkml"
# Aggregate awareness
include: "/lookml/aggregate/*.layer.lkml"
# Explores
include: "/lookml/int/*.lkml"
# Dashboards
include: "/lookml/dashboards/*.dashboard.lookml"
The order of includes matters: base must be included before staging, and staging before int, so each layer can reference the one below it.
The layering principle¶
The structure follows a strict bottom-up dependency order. Each layer only references layers below it:
┌──────────────────────────────────┐
│ dashboards/ │ What users see
├──────────────────────────────────┤
│ int/ (explores) │ Joins and business logic
├──────────────────────────────────┤
│ aggregate/ utils/ │ Performance + shared logic
├──────────────────────────────────┤
│ staging/ │ Your refinements
├──────────────────────────────────┤
│ base/ ← droughty generates │ Auto-synced from warehouse
└──────────────────────────────────┘
The key constraint: never put anything in base/ that you’d have to maintain manually. Everything you’d normally put in a base view — labels, tags, custom measures, access filters — belongs in staging/ instead. That way droughty lookml can safely overwrite base/ at any time.
droughty_project.yaml for this structure¶
To match the folder layout above, set these paths in your droughty_project.yaml:
profile: my_profile
lookml_path: lookml/base
lookml_base_filename: _base.layer
lookml_explore_filename: _explores
lookml_measures_filename: _measures
With lookml_pop configured, the PoP file lands in the same lookml/base/ folder:
lookml_pop:
views:
fct_orders:
- order_date
This generates lookml/base/_base_parameters.layer.lkml — the PoP filename is fixed and is not affected by lookml_base_filename.
See How to Use — LookML Period-over-Period (PoP) for full PoP setup details.
File naming conventions¶
Pattern |
Extension |
Contains |
|---|---|---|
|
|
Base views (droughty-generated) |
|
|
Staging refinements |
|
|
Explore definitions |
|
|
Aggregate awareness |
|
|
Utility dimensions |
|
|
LookML dashboards |
The double-underscore in dashboard filenames (commerce__retail_cockpit) is a convention for grouping dashboards by area in the Looker UI — Looker displays the first segment as a folder label.