> For the complete documentation index, see [llms.txt](https://docs.digit.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.digit.org/health/design/architecture/low-level-design/registries/product.md).

# Product

Product and product-variant registry design

## Overview

The Product service is the campaign catalogue for commodities. It records items that teams distribute or move, such as vaccines, bed nets, and deworming tablets.

It manages two related records:

* **Product** — the base commodity, such as `Albendazole tablet`. It includes its type, name, and manufacturer.
* **Product variant** — a specific packaged form, such as `Albendazole 400mg, blister of 10`. It has an SKU and variation label.

Other services reference a product variant by ID. This prevents each service from redefining the same commodity.

## Business Flow

* **During campaign setup**, the products and variants for the campaign are registered here once. This is a small, mostly one-time reference list — products are **created/edited deliberately by an admin**, not bulk-imported by field workers.
* **During the campaign (runtime)**, the service is mostly **read**: Stock validates that the commodity being moved is a real product variant; dashboards and pickers look up product/variant names by ID.
* The registry is the single source of truth for commodity identity, so a vaccine renamed here is renamed everywhere it is referenced.<br>

## Key APIs / Entry Points

### API endpoints

The base path is `/product`. Product endpoints use `/product/v1`. Variant endpoints use `/product/variant/v1`.

Use `_create`, `_update`, and `_search` for both record types. An update request can soft-delete a record through `apiOperation`.

<table><thead><tr><th width="312.49609375">Endpoint</th><th>Purpose</th></tr></thead><tbody><tr><td><code>POST /product/v1/_create</code></td><td>Register a product (single or list).</td></tr><tr><td><code>POST /product/v1/_update</code></td><td>Edit or soft-delete a product.</td></tr><tr><td><code>POST /product/v1/_search</code></td><td>Find products (by id, name, type…) with paging.</td></tr><tr><td><code>POST /product/variant/v1/_create</code></td><td>Register a product variant (validates its parent product exists).</td></tr><tr><td><code>POST /product/variant/v1/_update</code></td><td>Edit or soft-delete a variant.</td></tr><tr><td><code>POST /product/variant/v1/_search</code></td><td>Find variants (by id, product id, SKU…) with paging.</td></tr></tbody></table>

All writes enter through the REST API. The service validates and enriches each request. It then returns `202` and publishes an event.

The persister stores the event in Postgres. The indexer sends the event to Elasticsearch. The service does not write to Postgres directly.

Search endpoints read Postgres directly. They do not query Elasticsearch.

**Swagger Contract:**

{% embed url="<https://editor.swagger.io/?url=https://raw.githubusercontent.com/egovernments/health-campaign-services/master/docs/health-api-specs/contracts/registries/product.yml>" %}

#### Kafka topics

The API consumes no Kafka topics. It only publishes validated write events.

<table><thead><tr><th>Topic</th><th width="170.11328125">Dir</th><th>Purpose</th></tr></thead><tbody><tr><td><code>save-product-topic</code></td><td>out</td><td>Persist new products</td></tr><tr><td><code>update-product-topic</code></td><td>out</td><td>Persist product updates</td></tr><tr><td><code>save-product-variant-topic</code></td><td>out</td><td>Persist new product variants</td></tr><tr><td><code>update-product-variant-topic</code></td><td>out</td><td>Persist variant updates</td></tr></tbody></table>

## Dependencies

* **idgen** generates IDs for products and variants.
* **Product service** verifies that a variant's parent product exists.
* **health-services-common** and **health-services-models** provide shared models, validation, enrichment, and repository helpers.
* **Kafka** carries output events for asynchronous persistence.
* **egov-persister** writes product rows to Postgres from `save-*` and `update-*` topics.
* **egov-indexer** indexes products and variants in Elasticsearch.
* **Redis** supports the shared repository cache.
* **MDMS** provides the `product_master` configuration for reference lookups.

## Process flow

1. A client creates or updates a product or variant through the API.
2. The API validates and enriches the request.
3. The API returns `202` and publishes a Kafka event.
4. The persister writes to Postgres. The indexer updates Elasticsearch.

Persistence completes after the API response. Confirm writes through consumer logs or Postgres when troubleshooting.

#### Sequence diagrams

{% tabs %}
{% tab title="Product" %}

<figure><img src="https://content.gitbook.com/content/I0KFbSBTySIQ7TCOKnJF/blobs/v8wrTLa9PoAADdR8QDgv/product_create.png" alt=""><figcaption></figcaption></figure>

<figure><img src="https://content.gitbook.com/content/I0KFbSBTySIQ7TCOKnJF/blobs/YDaBdgk0TPYFDXVYBuOX/product_update.png" alt=""><figcaption><p>Product - Update</p></figcaption></figure>

<figure><img src="https://content.gitbook.com/content/I0KFbSBTySIQ7TCOKnJF/blobs/wPXsNWP7H12Yril1ft9h/product_search.png" alt=""><figcaption><p>Product - Search</p></figcaption></figure>
{% endtab %}

{% tab title="Variant" %}

<figure><img src="https://content.gitbook.com/content/I0KFbSBTySIQ7TCOKnJF/blobs/Rf6dNnh9YCUEhAVDYy2H/product_variant_create.png" alt=""><figcaption><p>Product Variant - Create</p></figcaption></figure>

<figure><img src="https://content.gitbook.com/content/I0KFbSBTySIQ7TCOKnJF/blobs/gTJ6v7yvgO0Q97zsUxaV/product_variant_update.png" alt=""><figcaption><p>Product Variant - Update</p></figcaption></figure>

<figure><img src="https://content.gitbook.com/content/I0KFbSBTySIQ7TCOKnJF/blobs/0a6mKSIvUmvEOjzddmXG/product_variant_search.png" alt=""><figcaption><p>Product Variant - Search</p></figcaption></figure>
{% endtab %}
{% endtabs %}

#### Data model

<figure><img src="https://2077406040-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FI0KFbSBTySIQ7TCOKnJF%2Fuploads%2FjIupjYfRSGzUJXO3pq87%2Fimage.png?alt=media&amp;token=10502d93-ac87-4c96-bdc6-b49736381c7e" alt=""><figcaption></figcaption></figure>

### Failure / Retry Handling

* **Asynchronous persistence:** A `202` response does not confirm the Postgres write. Check consumer logs and Postgres if a record is missing.
* **No batch rollback:** A failed record in a list does not roll back successful records.
* **Optimistic locking:** Updates require the current `rowVersion`. Stale versions are rejected.
* **Parent validation:** Variant creates and updates require an existing parent product.
* **Soft deletion:** Records are never hard-deleted. Set the appropriate `apiOperation` through the update endpoint.

{% hint style="warning" %}
Keep persister configuration current in every environment. Missing or stale topic configuration accepts API writes but prevents Postgres persistence.
{% endhint %}

### Known Risks & Limitations

* **No inbound bulk import:** There is no Kafka or file import. Use `_create` with an external script for large catalogues.
* **Free-text attributes:** `type`, `name`, `manufacturer`, `sku`, and `variation` have no database-level catalogue constraint.
* **Application-level integrity:** Postgres has no foreign key from a variant to its product. A product can be soft-deleted after variants exist.
* **Separate search and dashboard data:** Search reads Postgres. A stale Elasticsearch index affects dashboards and lookups, not `_search`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.digit.org/health/design/architecture/low-level-design/registries/product.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
