Insights

A Practical Unity Catalog Model for the Enterprise

DATALakeline Data7 min read

Most Unity Catalog rollouts fail the same way. The first workspace becomes the model, a few teams copy it, and two quarters later nobody can say which catalog is safe to drop, who can read customer data, or why a dashboard broke after a "harmless" rename. The fix is not a better tool. It is deciding the namespace and the access model before the first production table lands.

This is the model we recommend for organisations with more than one data domain and more than one environment.

Start from three questions

Before you create anything, answer three questions in writing:

  1. What is an environment? Usually dev, uat (or test) and prod. Some organisations add a sandbox for exploration that never promotes.
  2. What is a domain? A business area that owns its data: sales, finance, operations, HR. Domains map to teams, not to source systems.
  3. Who decides access? A data owner per domain, with requests and approvals recorded somewhere other than a chat thread.

If these answers are not stable, the catalog will not be either.

Environment-first catalogs

Use one catalog per environment per domain:

  • dev_sales, uat_sales, prod_sales
  • dev_finance, uat_finance, prod_finance

Inside each catalog, use schemas for layers: bronze, silver, gold, plus sandbox in dev only.

This looks verbose until you deploy. Promotion becomes a deployment concern, not a permissions concern: the same Databricks Asset Bundle targets dev_sales in development and prod_sales in production, with the catalog name as a variable. Nobody edits SQL to promote a pipeline, and nobody accidentally writes to production from a dev job, because the dev service principal has no grant there.

It also makes teardown honest. Dropping dev_sales is safe by construction. Dropping anything that starts with prod_ goes through a change request.

Grant to groups, never to people

Every grant goes to a group synced from your identity provider (Entra ID, Okta). Never to an individual user, and never to a workspace-local group.

SQL
-- applied by CI from a reviewed file, never typed into a notebook
GRANT USE CATALOG ON CATALOG prod_sales TO `grp-sales-readers`;
GRANT USE SCHEMA ON SCHEMA prod_sales.gold TO `grp-sales-readers`;
GRANT SELECT ON SCHEMA prod_sales.gold TO `grp-sales-readers`;

Joiners, movers and leavers are then handled where HR already handles them, in the identity provider. Access reviews become a list of group memberships rather than an archaeology project across thousands of grants.

A useful default set of groups per domain:

GroupGets
grp-<domain>-readersSELECT on gold
grp-<domain>-analystsSELECT on silver and gold
grp-<domain>-engineersMODIFY in dev and uat, read in prod
sp-<domain>-pipelinesThe service principal that writes to prod

Only service principals write to production. Humans read production; they change it through deployments.

Tag sensitive data once, mask it everywhere

Tag columns that hold personal or sensitive data where they are created, in silver:

SQL
ALTER TABLE prod_sales.silver.customers
  ALTER COLUMN email SET TAGS ('pii' = 'email');

Then enforce with column masks and row filters defined once in the catalog, keyed on group membership. A BI tool, a notebook and an AI agent all see the same masked value, because the rule lives with the data rather than in each consumer. Teams that put masking logic in Power BI end up maintaining it in five places, and the sixth place is where the leak happens.

PIPELINE
  1. Identity provider groups
  2. Grants applied by CI
  3. Tags on sensitive columns
  4. Masks and row filters
  5. Every consumer

Keep governance in git

Treat grants, tags and masks like any other code:

  • Store them in the repository next to the pipelines that create the tables.
  • Review them in pull requests, with the data owner as a required reviewer for production changes.
  • Apply them from CI with a service principal, so the audit log shows one identity making governance changes.

This gives you something auditors actually want: a history of who approved which access, when, and why.

Use what Unity Catalog records for you

Two features pay for the discipline above:

  • Lineage shows every downstream table, notebook and dashboard that depends on a table. Check it before you rename or drop anything.
  • System tables (system.access.audit, system.billing.usage) record access decisions and spend. Build a small dashboard over them in week one: who read sensitive tables, which jobs cost the most, which tables nobody has queried in 90 days.

Common mistakes to avoid

  • One catalog per team "for now". It never gets restructured.
  • Granting ALL PRIVILEGES to engineers in production to unblock a release. Fix the deployment instead.
  • Using the Hive metastore alongside Unity Catalog indefinitely. Plan the migration, use SYNC for external tables, and set a date after which nothing new lands in Hive.
  • Leaving ownership with the person who created the table. Transfer ownership to a group as part of the deployment.

Where to start

If you already have a sprawling workspace, do not try to fix everything at once. Pick one domain, create its three catalogs, move its gold tables first, and switch its consumers. The second domain goes faster, and by the third the pattern is the default.

If you want a second opinion on your current layout, we are happy to review it and write down what we would change.

  • #Unity Catalog
  • #Databricks
  • #Governance
  • #Security

Working on the same problem?

Tell us what you run today. Lakeline Data builds this kind of system.

Talk to us