Entity Parameters
Overview
The Entity Parameters module provides a flexible key-value configuration and feature-flag system. It allows business-level and entity-level (e.g., location) parameter overrides by linking entries from a Parameter Catalog to specific entities.
Architecture
entity-parameters/
├── entity-parameters.module.ts # NestJS module (DI wiring)
├── domain/
│ └── entity-parameters-repository.domain.ts # Port interface + DI token + parseBooleanParameterValue
├── application/
│ └── entity-parameters.service.ts # Use cases (CRUD + getBooleanForBusiness)
├── infrastructure/
│ ├── entity-parameters.repository.ts # Kysely DB adapter
│ └── feature-parameter.guard.ts # LoyaltyEnabledGuard, StoreCreditEnabledGuard
└── interfaces/
├── entity-parameters.controller.ts # REST endpoints (/entity-parameters)
├── dtos/
│ ├── create-entity-parameter.dto.ts
│ └── update-entity-parameter.dto.ts
└── query/
└── paginate-entity-parameters.query.ts
Dependency flow: Controller -> Service -> Repository (via domain port)
Domain Concepts
Parameter Catalog
A catalog of available parameters (e.g., LOYALTY_ENABLED, BARCODE_SCAN_INPUT_ENABLED). Each entry defines:
code— unique parameter keydataType— expected type (boolean,string,number)entityType— scope level (business,location)defaultValue/allowedValues— constraints
Entity Parameter
A concrete assignment of a catalog parameter to a business entity:
parameterCatalogId— links to the catalog entryparameterValue— JSON object, convention:{ "value": <actual_value> }businessId— owning businessentityType+entityId— target entity (nullentityId= business-wide default)
Priority Resolution
When resolving a parameter value for a business:
- Specific entity match (entityId = businessId) takes priority
- Business-wide default (entityId = null) is the fallback
- null if no parameter is set
Feature Guards
The module exports two NestJS guards for feature-gating:
| Guard | Parameter Code | Purpose |
|---|---|---|
LoyaltyEnabledGuard | LOYALTY_ENABLED | Gates loyalty program endpoints |
StoreCreditEnabledGuard | STORE_CREDIT_ENABLED | Gates store credit endpoints |
Both guards resolve businessId from: Firebase claims -> request body -> query params -> locationId DB lookup.
Available Parameter Codes
| Code | Type | Entity Type | Description |
|---|---|---|---|
SERVICE_BOOKING_ENABLED | boolean | business | Enable service booking |
SERVICE_BOOKING_REQUIRED | boolean | business | Require service booking in documents |
ASSIGN_CUSTOMER_FROM_BOOKING_PLATFORM | boolean | business | Auto-assign booking customer |
CASH_REGISTER_DISCREPANCY_APPROVAL_THRESHOLD | number | business | Approval threshold amount |
CASH_REGISTER_CASH_OUT_APPROVAL_THRESHOLD | number | business | Cash-out threshold amount |
REQUIRE_CASH_REGISTER_SESSION_FOR_ADD_DOCUMENT | boolean | business | Require cash session for docs |
LOYALTY_ENABLED | boolean | business | Enable loyalty program |
STORE_CREDIT_ENABLED | boolean | business | Enable store credit wallet |
BARCODE_SCAN_INPUT_ENABLED | boolean | location | Enable barcode scan input |
API Endpoints
All endpoints require Bearer token authentication.
| Method | Path | Description |
|---|---|---|
POST | /entity-parameters | Create entity parameter |
GET | /entity-parameters | List with pagination/search |
GET | /entity-parameters/:id | Get by ID |
PATCH | /entity-parameters/:id | Update (partial) |
DELETE | /entity-parameters/:id | Delete |
Create Entity Parameter
POST /entity-parameters
{
"parameterCatalogId": "<uuid>",
"parameterValue": { "value": true },
"businessId": "<uuid>",
"isActive": true,
"createdBy": "<uuid>",
"entityType": "business",
"entityId": "<uuid or omit for business-wide>"
}
List Entity Parameters
GET /entity-parameters?businessId=<uuid>&page=1&size=20&search=LOYALTY
Update Entity Parameter
PATCH /entity-parameters/:id
{
"parameterValue": { "value": false },
"updatedBy": "<uuid>"
}
Design Decisions
-
Parameter values are JSON objects (
{ "value": ... }) rather than raw primitives — this allows future extension with metadata (e.g.,{ "value": true, "overriddenAt": "..." }). -
parseBooleanParameterValuelives in the domain layer — it encodes business rules about how to interpret parameter values and is framework-agnostic. -
Repository is injected via DI token (
ENTITY_PARAMETERS_REPOSITORYSymbol) — the service depends on the domain port interface, not the concrete Kysely implementation. -
Feature guards use shared
resolveBusinessIdhelper — centralizes the multi-source businessId resolution logic.