Tessera RP

PX-09

Field Manual

The Semantic Layer

Seven layers sit between an ERP's tables and a number somebody acts on. Almost every analytics failure is a layer doing a job that belonged to another one. This is the stack, named — and the graph we are putting underneath it.

// 01

The Problem

Four vendors, three to six months, and an analytics engineer you cannot hire.

The standard answer to 'get the numbers out of the ERP' is a stack: an extract-and-load vendor, a warehouse, a modelling layer, and a BI tool. Four contracts, four billing models, and a data team to hold them together. Below roughly $200M of revenue, almost nobody can justify it, so the business runs on spreadsheets exported from the ERP — and those spreadsheets disagree with each other, because each one re-derives the same measure a slightly different way.

The instinct is to fix that with a smarter tool. It is not a tool problem. It is a layering problem: the definition of gross margin ends up living in a BI dashboard, so a second dashboard cannot reuse it; the join logic ends up in a spreadsheet, so nothing else can see it. Every one of those failures is a job done at the wrong altitude.

Extract & load

Priced per monthly active row — the bill grows with the business.

Warehouse

Priced per compute-second, on queries nobody profiled.

Modelling

Priced in analytics-engineer headcount, which is the scarce input.

BI

Priced per seat, so the people who need the number are the ones cut.

// 02

The Model

Seven layers, each with a contract the layer above can rely on.

Networking solved this shape of problem in 1984 by refusing to let any layer know how the one below it works. An application does not know whether it is on copper or fibre; it knows there is a socket. The layers are separable, and that is precisely why you can change the cable without rewriting the browser.

Enterprise data has the same shape and rarely gets the same discipline. Named as layers, the architecture stops being an opinion and starts being a contract — and the test of a contract is what you can swap without touching anything above it.

LayerNameWhat it ownsSwap it and…
L1SourceThe ERP's own tables — AxDB, BC's OData, NetSuite's SuiteQL…you have added an ERP, not a project
L2ConnectorAuth, pagination, watermarks, backoff, dead-letter…nothing above notices the API changed
L3LandingRaw payloads as JSONB, append-only, replayable…schema drift costs nothing and history survives
L4CanonicalAn ERP-agnostic star schema — the interoperability boundary…every metric above keeps working, unchanged
L5GraphEntities and the edges between them, typed…relationship questions stop needing new tables
L6Semantic229 governed metrics, 101 dimensions, one definition each…two dashboards cannot disagree about a number
L7SurfaceDashboards, Explore, Ask, Signals, alerts, exports…a new surface inherits every guarantee below it

L5 is the layer under construction — the star schema carries the relationships today as foreign keys. Everything else on this table is shipped and running at erp.par2labs.com.

// 03

Layer 4

The boundary where the ERP stops mattering.

Above the canonical star schema, nothing knows which ERP the row came from. That is the whole point of putting a boundary there: adding Dynamics 365 to a system that already spoke Business Central meant writing a connector and a mapper, and touching no metric, no dashboard and no alert.

The star schema also does one thing an ERP does not. Every order line carries seven cost components — product, inbound freight, duty, outbound freight, payment fee, marketplace fee, return provision — and landed cost and contribution margin are generated columns, computed by the database itself. No query can derive margin inconsistently, because no query derives it at all.

ERP-agnostic

Seven connectors land in one shape. Adding the eighth changes nothing above.

Entity-qualified keys

Two subsidiaries with a warehouse called GENERAL stay two warehouses.

Generated columns

Margin is computed once, by Postgres, not by whoever wrote the query.

Replayable

The raw tier is append-only, so a mapping fix is a re-run, not a re-extract.

// 04

Layer 6

Nothing writes SQL except the compiler.

Every read path in the product — a dashboard tile, an ad-hoc query, an alert evaluation, a scheduled export, the assistant — produces a QuerySpec: metrics and dimensions referenced by id, never SQL text. The spec is validated against the catalog and against the caller's own row-level scope, and only then does SQL exist, with every filter value bound as a parameter.

This is why the assistant is safe rather than merely supervised. It is not an LLM that writes SQL carefully; it is an LLM that cannot write SQL at all. The worst a prompt injection can produce is a QuerySpec, and a QuerySpec is checked against what the caller could already see. There is no code path from model output to a query string.

The second consequence matters more day to day: a metric has exactly one definition. Two dashboards showing 'Contribution Margin %' resolve the same catalog entry, so they cannot drift apart. Ratio metrics are computed as SUM(numerator)/SUM(denominator) at result grain rather than as an average of ratios — the single most common way a correct-looking BI number is wrong.

01

A caller builds a QuerySpec

Metrics and dimensions by id, a window, filters. A person, a tile, an alert rule or the model — the shape is the same for all four.

02

Validation against the catalog

An id that does not exist is rejected here. So is a metric the caller's role may not read.

03

Row-level scope is appended

Mandatory filters from the caller's membership, added after their own — so nobody can widen their own scope.

04

SQL is emitted and bound

Catalog fragments, engine-neutral, with values as parameters. This is the only place SQL is written in the product.

// 05

Layer 5 · In build

Putting a graph underneath it.

A star schema is the right shape for aggregation and the wrong shape for reach. 'What did we sell' is a sum over one fact table. 'Which customers are exposed if this supplier fails' is customer → order line → product → component → purchase order → supplier, and the number of hops is not known when the question is asked. In SQL that is a recursive query written by hand, per question, by someone who can write one.

Tessera DB — our own native graph engine, already in the Armory — is designed for exactly that traversal. Putting it at Layer 5 does not replace the star schema; it sits beside it, fed from the same canonical rows, and answers the questions the star schema answers badly. The semantic layer above stays where it is: the compiler learns a second target, and a metric definition does not change.

This is the layer being built, and it is written here as intent rather than as inventory. Everything above and below it is running today.

Traversal

Multi-tier supplier exposure, component-level recall, circular trade — one pattern, any depth.

Ontology

Entities and relationships typed as first-class, so a new question needs no new fact table.

Entity resolution

The same supplier under two ERP codes joined by an evidenced edge, not a destructive key rewrite.

Lineage

Source record → warehouse row → metric → tile, as a graph you can walk in both directions.

Algorithms

Supplier concentration as centrality, customer structure as community detection, fraud as cycles.

Vector + graph

Retrieval by meaning over the ontology, while the arithmetic stays with the compiler.

“The graph changes what can be asked. It does not change who is allowed to ask it.”

// 06

The Payoff

What a layered stack actually buys the business.

The argument for layers is usually made to engineers and lands as architecture taste. It is not taste. Each contract in the stack pays out as something an operator can point at.

One number

Contribution margin means the same thing in every dashboard, alert and export.

One hour, not one quarter

A finance lead connects a source and has dashboards, without an analytics engineer.

Add an ERP, keep the work

A new connector inherits 229 metrics and eleven dashboards on day one.

Answers that cite themselves

Every figure names the metric, the window, the freshness and how sure it is.

Isolation that survives a bug

Postgres row-level security, so a forgotten filter returns nothing, not somebody else's books.

// 07

Deployed

Where it is running: Dynamics 365.

The first production surface is Dynamics 365 Finance & Operations. The connector reads SQL Server rather than the OData API, because that is where Microsoft's own analytics guidance points — a BYOD database, a Data Lake staging database, or a read replica — and because pulling three hundred thousand inventory transactions through a transactional API competes with the users posting the next ones.

Every query is scoped to a legal entity, and that is not optional: DATAAREAID sits on every business table, and a query that omits it reads across every company in the deployment and returns a number belonging to none of them, silently. The connector refuses to run without at least one legal entity configured rather than defaulting to 'all'.

A deployment ships as a pack: a build whose Connections page offers Dynamics 365 and a CSV, whose warehouse holds legal entities, sites, warehouses, bins and dimensional stock, and whose personas are the seven that read them.

Read path

BYOD, Data Lake staging, or an AxDB read replica — whichever the deployment has.

Scoped

Every stream filtered by legal entity, parameterised, never interpolated.

Dimensional stock

Site, warehouse, location and inventory dimensions, not a single flat quantity.

Also spoken

Business Central, NetSuite, Acumatica, Shopify, Amazon SP-API, CSV/REST.

// FAQ

Straight Answers

Asked and answered.

What is a semantic layer?

A semantic layer is the tier that holds business definitions — what 'net revenue' means, what 'contribution margin' excludes, what grain a ratio is computed at — separately from both the storage underneath it and the dashboards above it. Every surface resolves the same definition by id, so two reports cannot disagree about a number. In Tessera RP it is a catalog of 229 governed metrics and 101 dimensions, and a compiler that turns a validated query specification into parameterised SQL.

How is a semantic layer different from text-to-SQL?

Text-to-SQL asks a language model to write a query and then hopes the query is both correct and permitted. A semantic layer never lets the model near SQL: the model emits a query specification referencing metrics and dimensions by id, which is validated against the catalog and against the caller's own data scope before any SQL exists. An invented table fails validation, and a prompt injection cannot reach data the caller could not already see.

Why describe enterprise data as an OSI model?

Because the useful property of the OSI model is not the number seven, it is that each layer publishes a contract and hides its implementation. Applied to enterprise data it means the ERP can be swapped at Layer 1 without touching a metric at Layer 6, and the storage engine can be swapped at Layer 4 or 5 without touching a dashboard at Layer 7. Where those boundaries are absent, a margin definition ends up trapped inside one BI dashboard and every other surface re-derives it differently.

What does a graph database add that a star schema cannot?

Reach. A star schema aggregates one fact table efficiently and answers relationship questions badly, because the number of hops is unknown when the question is asked — multi-tier supplier exposure, component-level recall, circular trading, the blast radius of one failed connector. A graph expresses those as a single pattern at any depth, and makes graph algorithms available: supplier concentration as centrality, customer structure as community detection. The plan is to run Tessera DB at Layer 5, fed from the same canonical rows, with the semantic layer above it unchanged.

Why is ERP-reported margin usually overstated for e-commerce?

Because an ERP books revenue and product cost but typically not outbound shipping, payment processing, marketplace referral and fulfilment fees, or the eventual cost of returns. On a direct-to-consumer line those routinely total 18–30% of net revenue. Tessera RP decomposes seven cost components on every order line and reports contribution margin, which is why its margin figure and the ERP's are expected to differ.

Which ERPs does Tessera RP connect to?

Dynamics 365 Finance & Operations, Business Central, NetSuite and Acumatica on the ERP side; Shopify and Amazon SP-API on the channel side; and a generic CSV/REST connector for the data every engagement has that the ERP does not hold. Adding another is a connector and a mapper — the canonical schema, the metric catalog and the dashboards above it are untouched.

How is one customer's data kept away from another's?

By Postgres row-level security, enforced in the database rather than by application WHERE clauses. The application connects as a role that is neither superuser nor BYPASSRLS and holds no DDL rights, and every tenant query runs inside a transaction that sets a transaction-local setting the policies compare against. A forgotten predicate in application code therefore returns zero rows rather than another customer's financials.

The layers are the product.

Tessera RP is what the stack looks like when it ships as one application: the ingestion, the warehouse model, the metric definitions and the dashboards, rather than the tools to build them. Open the running deployment and read the numbers off a seeded workspace.