Tags Module
Overview
The tags module manages a global catalog of tags used to classify addons in FlowPOS. Unlike most modules, tags are not business-scoped — they are system-wide entities shared across all businesses.
Tags are linked to addons via the addon_tag join table (managed by the separate addon-tags module).
Architecture
The module follows Hexagonal Architecture with strict layer boundaries.
tags/
├── tags.module.ts # NestJS module wiring
├── domain/
│ └── tags-repository.domain.ts # ITagsRepository port, TAGS_REPOSITORY token, sortable keys
├── application/
│ └── tags.service.ts # Use cases (CRUD + pagination)
├── infrastructure/
│ └── tags.repository.ts # Kysely DB adapter (implements port)
└── interfaces/
├── tags.controller.ts # HTTP adapter
├── dtos/
│ ├── create-tag.dto.ts
│ └── update-tag.dto.ts
└── query/
└── paginate-tags.query.ts
Layer responsibilities
| Layer | Responsibility |
|---|---|
domain/ | Repository port (ITagsRepository), TAGS_REPOSITORY injection token, and sortable key constants. |
application/ | TagsService — orchestrates CRUD operations and pagination. |
infrastructure/ | TagsRepository — Kysely queries against the tag table. Implements ITagsRepository. |
interfaces/ | TagsController — maps HTTP requests to service calls. Thin layer with no business logic. |
Dependency rule
interfaces → application → domain ← infrastructure
Domain Concepts
Tag
A named label used to classify addons by purpose or category.
| Field | Type | Description |
|---|---|---|
id | UUID | Primary key (auto-generated) |
uniqueName | string | Machine-readable identifier (e.g., business, customer) |
name | string | Display name |
description | string | Description of the tag's purpose (nullable) |
isActive | boolean | Defaults to true |
createdBy | UUID | FK to user |
updatedBy | UUID | FK to user (nullable) |
createdAt | timestamptz | Auto-set on creation |
updatedAt | timestamptz | Set on update |
Known Tag UniqueNames
Defined in TagUniqueName enum (packages/backend/database/src/enums/tag.enums.ts):
| Value | Purpose |
|---|---|
business | Business configuration addons |
customer | Customer-facing addons |
payment_gateway | Payment integration addons |
personalization | UI/UX customization addons |
API Endpoints
All endpoints require Bearer token authentication and are scoped to PolicyResource.Tag for RBAC.
POST /tags
Create a new tag.
Body:
{
"uniqueName": "custom_tag",
"name": "Custom Tag",
"description": "A custom tag for addon classification.",
"isActive": true,
"createdBy": "<uuid>"
}
Responses: 201 Created | 400 Invalid body | 401 Unauthorized
GET /tags
List tags with pagination, search, and sorting.
Query parameters:
| Param | Required | Description |
|---|---|---|
page | No | Page number (default: 1) |
size | No | Page size (default: 10) |
search | No | Case-insensitive search on name and uniqueName |
orderBy | No | Sort field (name, uniqueName) |
order | No | asc or desc |
Responses: 200 Paginated list | 401 Unauthorized
GET /tags/:id
Get a single tag by UUID.
Responses: 200 Found | 404 Not found | 401 Unauthorized
PATCH /tags/:id
Partially update a tag.
Body:
{
"name": "Updated Name",
"updatedBy": "<uuid>"
}
Responses: 200 Updated | 400 Invalid body | 404 Not found | 401 Unauthorized
DELETE /tags/:id
Delete a tag permanently.
Responses: 200 Deleted | 404 Not found | 401 Unauthorized
Design Decisions
- Global scope (no businessId): Tags classify addon capabilities, which are system-level concerns — not business-specific data.
- Dependency injection via Symbol token:
TAGS_REPOSITORYfollows the same pattern asBRANDS_REPOSITORY, enabling easy testing and swapping of repository implementations. - Sortable keys in domain layer: Placed in
domain/tags-repository.domain.tsto avoid the interface layer leaking into domain — respecting hexagonal dependency rules.
Related Modules
- Addon Tags — many-to-many relationship between addons and tags (
addon_tagtable) - Addons — the entities being classified by tags