Modules System
The modules system controls feature enablement across the FlowPOS platform. It consists of three backend modules working together:
| Module | Table | Purpose |
|---|---|---|
modules | module | Global catalog of feature modules (POS, Inventory, Billing, etc.) |
business-modules | business_module | Many-to-many enablement of modules per business |
forms | form | Navigation page definitions linked to modules |
Architecture
All three modules follow hexagonal architecture:
domain/ → Repository port interface (IModulesRepository)
application/ → Service with business logic
infrastructure/ → Kysely repository adapter
interfaces/ → Controller, DTOs, query objects
Dependencies are injected via Symbol tokens (MODULES_REPOSITORY, BUSINESS_MODULES_REPOSITORY, FORMS_REPOSITORY).
Domain Concepts
Module
A module represents a high-level feature area. There are 18 system-seeded modules:
| Module | UUID |
|---|---|
| Settings | 8da5d77c-2e3b-4c85-b225-debffa5138d8 |
| POS | e1db4d48-3b04-40bc-8451-d84fdd6293b2 |
| Inventory | 5a47f719-6d63-4edc-b553-b90d4a3cea19 |
| Goods & Services | a43a7bea-f4b6-461c-b6e0-7e349eb048ed |
| Billing | 7a45fa4e-dfe3-4e49-b90f-86bc0ff28582 |
| Accounts Receivable | 4ed0bdeb-cf1d-460f-a38e-35b2c4a3f699 |
| Accounts Payable | 06560d1a-9b49-49cb-8990-db5a90685f80 |
| Purchase | c4561699-ace1-415b-b1ca-81b19f6dfc20 |
| Production | d3e38289-3da9-428b-a2f7-516c9408c7f0 |
| Customers | b84f3caa-ec30-4378-9a02-8763c7221f7b |
| Reports | 011d26b6-1266-40e8-bca3-50ce71871f77 |
| Payroll | 8f3f7a0e-1f9b-4821-af10-15462a31f2e8 |
| Admin | a0e48832-47d6-465d-9657-675650c332c0 |
| System | f6fac323-e52c-4628-9200-20bfe28ee489 |
| Service Bookings | 7ffde23e-0688-4b2b-9cde-132cb531280a |
| Communication | 2b69cc58-57ac-4e1b-9488-bc660f18e4a9 |
| Restaurant | 6c483495-c30a-42b5-8522-ad4e440cb9fa |
| Data Import | a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d |
Modules can be system-wide (businessId = null) or business-specific (businessId set).
Business Module
A business_module record links a business to a module, controlling whether that feature is enabled. When a new business is created, the OnCreateBusinessEvent listener automatically seeds 17 default associations.
Form
A form represents a navigation entry (route/page) in the PWA, linked to a module via moduleId. Forms define the dynamic menu structure.
Key Flows
Business Creation → Module Enablement
Business Created
→ OnCreateBusinessEvent emitted
→ BusinessModulesService.handleBusinessCreateEvent()
→ 17 default business_module records created
Module → Form → Navigation
Module (feature area)
→ Forms (pages within module)
→ PWA navigation menu
→ Metabase dashboards (optional)
Soft Delete
Deleting a module performs a soft delete (isActive = false) on both the module record and all related business_module records.
API Endpoints
Modules (/modules)
| Method | Path | Description |
|---|---|---|
POST | /modules | Create a feature module |
GET | /modules | List all modules |
GET | /modules/:id | Get module by ID |
GET | /modules/business/:businessId | List modules owned by a business |
GET | /modules/with-relation/:businessId | List all modules with business enablement status |
PATCH | /modules/:id | Update a module |
DELETE | /modules/:id?businessId= | Soft-delete a module |
Business Modules (/business-modules)
| Method | Path | Description |
|---|---|---|
POST | /business-modules | Create a business-module association |
GET | /business-modules | List with pagination (filter by businessId) |
GET | /business-modules/:id | Get by ID |
PATCH | /business-modules/:id | Update association |
PATCH | /business-modules/disable-module/:id?businessId= | Soft-disable a module for a business |
DELETE | /business-modules/:id | Hard-delete association (204) |
Forms (/forms)
| Method | Path | Description |
|---|---|---|
POST | /forms | Create a navigation form |
GET | /forms | List with pagination, search, sorting |
GET | /forms/:id | Get by ID |
PATCH | /forms/:id | Update a form |
DELETE | /forms/:id | Delete a form |
Adding New Modules
When adding a new system module:
- Add the UUID to
packages/backend/database/src/seeds/data/global/module.data.ts - Create a migration to insert the module record (use
onConflict().doNothing()) - Create a migration for associated forms
- Optionally backfill
business_modulefor existing businesses - Add the module to
defaultBusinessModulesinBusinessModulesService.handleBusinessCreateEvent()
Design Decisions
- Soft delete over hard delete: Modules use
isActive = falseto preserve referential integrity with forms, dashboards, and business associations. - Event-driven module seeding: Decouples business creation from module setup via
OnCreateBusinessEvent. - System vs. business modules: System modules (
isSystem = true,businessId = null) are platform-wide; business modules are custom per-business features. with-relationendpoint: Returns all modules with a LEFT JOIN to business_module, enabling the admin UI to show both enabled and available modules in one request.