Design

Documentation for Modules

Platform Settings — Design Document

Aggregator module that provides administrative CRUD pages for domain core entities (tax classes, brands, categories, tags).

Architecture

Platform Settings is an aggregator module — it owns no entities of its own. Instead, it exposes CRUD pages that delegate all data operations to Domain Core lib services.

platform-settings (Business Module)
├── Consumes: @libs-core/fiscal        → TaxClass CRUD
├── Consumes: @libs-core/brand-registry → Brand CRUD
├── Consumes: @libs-core/taxonomy      → Category CRUD, Tag CRUD
├── Consumes: @libs-core/ui-framework  → Shell components, form controls
└── Consumes: @libs-core/module-base   → CRUD composables, module contract

Key Characteristic: No Own State

Platform Settings does not have:

This makes it an ideal reference for aggregator-pattern modules.


Dependencies

Dependency Import Path Usage
fiscal @libs-core/fiscal TaxClass model, FiscalApiService, TaxClassCreated/Updated/Deleted events
brand-registry @libs-core/brand-registry Brand model, BrandApiService, BrandCreated/Updated/Deleted events
taxonomy @libs-core/taxonomy Category/Tag models, TaxonomyCategoryApiService, TaxonomyTagApiService, events
module-base @libs-core/module-base BrowserModuleContract, ModuleDecorator, useCrudList, useCrudForm, useCrudDetail
ui-framework @libs-core/ui-framework GgsCrudListShellComponent, GgsCrudFormShellComponent, GgsFilterPanelComponent
security @libs-core/security authGuard, contextGuard, permissionGuard
log-system @libs-core/log-system LoggerService

Navigation: Hierarchical

Platform Settings uses hierarchical navigation — a parent "Settings" group with 4 children. The parent item has no route of its own; it serves only as a grouping container.

navigation: {
  menuItems: [
    {
      id: 'settings-parent',
      label: 'Settings',
      icon: 'cog',
      order: 900,
      permissions: ['settings:read'],
      children: [
        { id: 'settings-tax', label: 'Tax Classes', icon: 'percent', route: 'tax-classes', order: 1, permissions: ['settings:fiscal:manage'] },
        { id: 'settings-brands', label: 'Brands', icon: 'tag', route: 'brands', order: 2, permissions: ['settings:brands:manage'] },
        { id: 'settings-categories', label: 'Categories', icon: 'sitemap', route: 'categories', order: 3, permissions: ['settings:taxonomy:manage'] },
        { id: 'settings-tags', label: 'Tags', icon: 'tags', route: 'tags', order: 4, permissions: ['settings:taxonomy:manage'] },
      ],
    },
  ],
},

Sidebar rendering:

▸ Settings
  ├── Tax Classes
  ├── Brands
  ├── Categories
  └── Tags

Permissions

Permission ID Description
settings:read View any settings page
settings:fiscal:manage Create, edit, delete tax classes
settings:brands:manage Create, edit, delete brands
settings:taxonomy:manage Create, edit, delete categories and tags

The module uses 4 granular permissions — one per sub-section — plus a general read permission for viewing.


Components

Tax Classes Section

Component Description Composable
TaxClassListComponent Paginated list with search, inline status filter useCrudList<TaxClass> with FiscalApiService
TaxClassFormComponent Create/edit form (name, code, rate, description, isDefault) useCrudForm<TaxClass> with FiscalApiService

Brands Section

Component Description Composable
BrandListComponent Paginated list with search, active filter useCrudList<Brand> with BrandApiService
BrandFormComponent Create/edit form (name, slug, description, website, logo) useCrudForm<Brand> with BrandApiService

Categories Section

Component Description Composable
CategoryManagerComponent Tree view + list hybrid for category management useCrudList<Category> with TaxonomyCategoryApiService
CategoryFormComponent Create/edit form (name, slug, description, parent selector) useCrudForm<Category> with TaxonomyCategoryApiService
CategoryDetailComponent Detail view with subcategories list useCrudDetail<Category> with TaxonomyCategoryApiService

Tags Section

Component Description Composable
TagListComponent Paginated list with search, scope filter, color display useCrudList<Tag> with TaxonomyTagApiService
TagFormComponent Create/edit form (name, slug, color picker, scope) useCrudForm<Tag> with TaxonomyTagApiService

Routes

export const platformSettingsRoutes: Routes = [
  {
    path: '',
    children: [
      { path: '', redirectTo: 'tax-classes', pathMatch: 'full' },
      // Tax Classes
      { path: 'tax-classes', component: TaxClassListComponent, canActivate: [authGuard, contextGuard, permissionGuard('settings:fiscal:manage')] },
      // Brands
      { path: 'brands', component: BrandListComponent, canActivate: [authGuard, contextGuard, permissionGuard('settings:brands:manage')] },
      // Categories
      { path: 'categories', component: CategoryManagerComponent, canActivate: [authGuard, contextGuard, permissionGuard('settings:taxonomy:manage')] },
      { path: 'categories/create', component: CategoryFormComponent, canActivate: [authGuard, contextGuard, permissionGuard('settings:taxonomy:manage')] },
      { path: 'categories/:id', component: CategoryDetailComponent, canActivate: [authGuard, contextGuard, permissionGuard('settings:read')] },
      { path: 'categories/:id/edit', component: CategoryFormComponent, canActivate: [authGuard, contextGuard, permissionGuard('settings:taxonomy:manage')] },
      // Tags
      { path: 'tags', component: TagListComponent, canActivate: [authGuard, contextGuard, permissionGuard('settings:taxonomy:manage')] },
    ],
  },
];

Note: Tax Classes, Brands, and Tags use inline editing (modal/inline form on the list page), so they only need a list route. Categories have a full CRUD flow (list + create + detail + edit) because of the hierarchical tree structure.


Mock API

Platform Settings does not define its own mock API routes. It reuses the mock APIs from domain core libs:

Domain Core Lib Mock API Source Endpoints
@libs-core/fiscal fiscalMockAPI /api/fiscal/tax-classes/*
@libs-core/brand-registry brandRegistryMockAPI /api/brands/*
@libs-core/taxonomy taxonomyMockAPI /api/taxonomy/categories/*, /api/taxonomy/tags/*

All three are already registered in mock-config.ts.


Module Registration

@ModuleDecorator({
  id: 'platform-settings',
  version: '1.0.0',
  name: 'Platform Settings',
  description: 'Administrative CRUD pages for tax classes, brands, categories, and tags',
  basePath: 'settings',
  repositories: [],
  relations: [],
  permissions: [
    { id: 'settings:read', name: 'settings:read', resource: 'settings', action: 'read' },
    { id: 'settings:fiscal:manage', name: 'settings:fiscal:manage', resource: 'settings', action: 'fiscal:manage' },
    { id: 'settings:brands:manage', name: 'settings:brands:manage', resource: 'settings', action: 'brands:manage' },
    { id: 'settings:taxonomy:manage', name: 'settings:taxonomy:manage', resource: 'settings', action: 'taxonomy:manage' },
  ],
  navigation: { /* hierarchical as shown above */ },
  tags: ['platform-settings', 'admin', 'configuration'],
})
@Injectable({ providedIn: 'root' })
export class PlatformSettingsModule implements BrowserModuleContract {
  readonly manifest: ModuleManifest = {
    id: 'platform-settings',
    version: '1.0.0',
    embeddableComponents: [],  // No widgets — admin module
    publicActions: [],          // No cross-module actions
  };

  readonly routes = platformSettingsRoutes;

  configure<T>(config: T): Observable<void> { return of(void 0); }
  initialize(): Observable<void> { return of(void 0); }
  start(): Observable<void> { return of(void 0); }
  stop(): Observable<void> { return of(void 0); }
  destroy(): Observable<void> { return of(void 0); }
}

When to Use This Pattern

Create an aggregator module when:

Do NOT create an aggregator if: