Saltar al contenido principal

Business Modules

Overview

The business-modules module manages the many-to-many relationship between a business and the global module catalog. Each record indicates that a specific feature module (e.g., POS, Restaurant, Inventory) is associated with a business, and whether it is currently active.

When a new business is created, 17 default modules are automatically seeded via an event listener (OnCreateBusinessEvent).


Domain Concepts

ConceptDescription
BusinessModuleJoin record linking a business to a module. Carries isActive to enable/disable the feature.
Module catalogGlobal list of available feature modules, seeded at deploy time from packages/backend/database/src/seeds/data/global/module.data.ts.
isActiveSoft enable/disable flag. Hard deletes are also supported but uncommon.
Default modulesThe 17 modules seeded for every new business: Settings, Billing, Goods & Services, POS, Admin, Accounts Receivable, Accounts Payable, Production, Purchase, Service Bookings, Payroll, Customers, Inventory, Reports, Communication, Restaurant, Data Import.

Architecture

The module follows strict Hexagonal Architecture:

interfaces/            ← HTTP adapter (controller, DTOs, query)
application/ ← Use cases (service)
domain/ ← Port interface + injection token (framework-agnostic)
infrastructure/ ← Kysely repository (DB adapter)

Dependency flow

BusinessModulesController

BusinessModulesService
↓ (injected via BUSINESS_MODULES_REPOSITORY token)
IBusinessModulesRepository ← BusinessModulesRepository (Kysely)

The service depends on the IBusinessModulesRepository interface (domain port), not the concrete Kysely implementation. The binding is configured in BusinessModulesModule using a NestJS custom provider with the BUSINESS_MODULES_REPOSITORY symbol token.

Key design decisions

  • Interface-based injection — The service is decoupled from Kysely via @Inject(BUSINESS_MODULES_REPOSITORY). This enables adapter swaps and testing with mocks.
  • Domain purity — The domain port contains no Kysely types (Transaction, SimplifySingleResult). Return types use plain TypeScript (T | undefined instead of SimplifySingleResult<T>).
  • Audit fields from auth tokencreatedBy and updatedBy are resolved from the bearer token via the @UserId() decorator. They are not accepted in the request body, preventing client impersonation.
  • SortableBusinessModuleKey is defined in the domain layer (not the interface layer) to keep the port free of UI concerns.
  • createdAt/updatedAt are server-managed; they are not accepted in the create/update DTOs.

Main Use Cases

List active business modules

GET /business-modules?businessId=<uuid>&size=50

Returns all active (isActive=true) module associations for a business. The businessId filter is required in practice; without it the query returns all records across all businesses.

Create a business module association

POST /business-modules

Manually associates a module with a business. Most modules are seeded automatically on business creation — this endpoint is primarily for admin or migration use. The createdBy field is set from the authenticated user's token.

Body:

{
"businessId": "550e8400-e29b-41d4-a716-446655440001",
"moduleId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Get a business module by ID

GET /business-modules/:id

Returns a single business module record. Returns 404 if not found.

Update a business module

PATCH /business-modules/:id

Partially updates any field on the association (e.g., toggling isActive). The updatedBy field is set from the authenticated user's token.

Body (example — toggle off):

{ "isActive": false }

Disable a business module (soft delete)

PATCH /business-modules/disable-module/:id?businessId=<uuid>

Sets isActive = false. The acting user is resolved from the auth token. Returns the updated record including the related module entity.

Prefer this over hard delete when the module may need to be re-enabled later.

Delete a business module (hard delete)

DELETE /business-modules/:id

Permanently removes the association. Returns 204 No Content. Cannot be undone.


API Endpoints

MethodPathStatusDescription
POST/business-modules201Create a business module association
GET/business-modules200List active modules for a business (paginated)
GET/business-modules/:id200Get by ID
PATCH/business-modules/:id200Update (partial)
PATCH/business-modules/disable-module/:id200Soft-disable (sets isActive=false)
DELETE/business-modules/:id204Hard delete

All endpoints require a valid Firebase bearer token (Authorization: Bearer <token> or flowpos-id-token cookie).


Bruno API Collection

Requests are in api-client/flowpos/collections/business-modules/.

FileOperation
list business modules.ymlGET /business-modules
create business module.ymlPOST /business-modules
get business module by id.ymlGET /business-modules/:id
update business module.ymlPATCH /business-modules/:id
disable business module.ymlPATCH /business-modules/disable-module/:id
delete business module.ymlDELETE /business-modules/:id