# Describe Your Data Product

Describe the data product behind the contracts with ODPS.

Source: https://learn.datacontract.com/en/data-product/

Your data contracts describe the *interface* of your data: tables, types, quality rules.
Consumers want more: *What is this data for? Who owns it? Which versions exist, and which one should I use?*
That is the job of the **data product**, described with the [Open Data Product Standard](https://bitol-io.github.io/open-data-product-standard/latest/) (ODPS).

**You will learn:**
- Understand the difference between a data product and a data contract
- Describe the Orders data product with ODPS 1.1, including its type and output ports
- Validate the description with the Data Product CLI

> **Data product vs. data contract**
>
> A **data product** is data that a team owns and offers to others *as a product*: with a purpose, an owner, support, and a lifecycle.
> It offers its data through **output ports**, each described by a **data contract**.
>
> There is **one** Orders data product, even though it offers **two** contract versions (`orders_v1` and `orders_v2`).
> The product is the stable unit of ownership. Its ports and contracts evolve.

_Scenario: the Orders data product (output ports orders_v1 and orders_v2) is consumed by the SKU Sales data product, which the purchasing team uses._

## Create the data product

### Create the file

The Data Product CLI creates a starter file:

macOS / Linux:

```bash
dataproduct init orders.odps.yaml
```

Windows (PowerShell):

```powershell
dataproduct init orders.odps.yaml
```

Output:

```text
📄 data product written to orders.odps.yaml
```

Open `orders.odps.yaml` in your IDE. It contains placeholders and commented-out sections.
Replace the top part with the fundamentals of your Orders product:

```yaml
apiVersion: v1.1.0
kind: DataProduct
id: orders
name: Orders
version: 1.0.0 # the version of the data product, independent of the contract versions
status: active
type: sourceAligned
domain: ecommerce
description:
  purpose: # what is this data product for?
  limitations: # what should consumers know before using it?
```

Replace the `purpose` and `limitations` comments with real text: what does a consumer need to know before requesting access?

> **Data product types (new in ODPS 1.1)**
>
> `type` tells consumers how a product is aligned:
>
> - **`sourceAligned`**: exposes data close to an operational system, like Orders
> - **`aggregate`**: combines several sources
> - **`consumerAligned`**: built for a specific use case, like the SKU Sales product in [Part B](https://learn.datacontract.com/en/contract-first/)

### Add the output ports

Add one **output port** per data contract. The `contractId` must match the contract's `id`:

```yaml
outputPorts:
  - name: orders_v1
    description: Orders and line items tables in PostgreSQL (v1, superseded by v2)
    deprecated: true
    version: 1.0.0
    contractId: orders_v1
  - name: orders_v2
    description: # ...
    version: 2.0.0
    contractId: orders_v2
```

Write the description for `orders_v2` yourself. Remove the example output port from `dataproduct init`.

`deprecated: true` (new in ODPS 1.1) marks the `orders_v1` port as no longer recommended. It stays documented for existing consumers, while new consumers pick `orders_v2`.

### Add team and support

Add `team` and `support`. Reuse what you defined in your contracts:

```yaml
team:
  name: order_data_team
  members:
    - username: owner@example.com
      role: Owner

support:
  - channel: "#order-data-help"
    url: https://example.slack.com/archives/order-data-help
    tool: slack
```

## Validate

### Lint the data product

Validate against the official ODPS JSON schema:

macOS / Linux:

```bash
dataproduct lint orders.odps.yaml
```

Windows (PowerShell):

```powershell
dataproduct lint orders.odps.yaml
```

Output:

```text
✅ Data product is valid against ODPS v1.1.0
🟢 Data product is valid.
```

Make it fail once: change `kind: DataProduct` to `kind: DataProdukt` and lint again:

macOS / Linux:

```bash
dataproduct lint orders.odps.yaml
```

Windows (PowerShell):

```powershell
dataproduct lint orders.odps.yaml
```

Output:

```text
❌ Check that data product is valid against ODPS v1.1.0: kind: 'DataProdukt' is not one of
['DataProduct']
🔴 Data product is invalid.
```

The CLI names the wrong field and exits with code `1`. Revert afterwards.

**Solution: orders.odps.yaml**

```yaml title=orders.odps.yaml
apiVersion: v1.1.0
kind: DataProduct
id: orders
name: Orders
version: 1.0.0
status: active
type: sourceAligned
domain: ecommerce
description:
  purpose: Source-aligned data product exposing orders and line items from the e-commerce platform.
  limitations: Contains PII (customer email addresses). Access requires approval by the Order Data Team.
tags: ['orders', 'ecommerce']
outputPorts:
- name: orders_v1
  description: Orders and line items tables in PostgreSQL (v1, superseded by v2)
  deprecated: true
  version: 1.0.0
  contractId: orders_v1
- name: orders_v2
  description: Orders and line items tables in PostgreSQL, including quantity (v2)
  version: 2.0.0
  contractId: orders_v2
team:
  name: order_data_team
  members:
  - username: owner@example.com
    role: Owner
support:
- channel: "#order-data-help"
  url: https://example.slack.com/archives/order-data-help
  tool: slack
```

> **Lint vs. test**
>
> `dataproduct lint` checks that the file is *well-formed* according to the standard. It doesn't connect to a database.
> Whether the data behind an output port keeps its promise is checked by `datacontract test` on the contract.

**Quick check:** Your Orders product releases orders_v3 next year. What changes in orders.odps.yaml?

- You create a new data product orders_v3
- You add an output port that references the orders_v3 contract (correct)
- Nothing, the data product doesn't reference contracts
- You replace the orders_v2 port with orders_v3 right away

The data product stays the same. A new contract version becomes a new output port, and old ports are deprecated and later removed once their contracts are retired.

## Bonus

- `orders_v1` is `retired`. Should its deprecated output port stay in the product description, or be removed? Weigh transparency for existing consumers against a clean catalog.
- ODPS 1.1 also supports a `context` block for AI agents at product and output port level, like ODCS 3.2. Add `instructions` that tell an agent which port to use.
- Add `tags` to the data product, e.g. `['orders', 'ecommerce']`, and lint again.
