Skip to content

Multi-Instance Architecture

One of the unique aspects of Syneo/Barcoding is its three-instance architecture - instead of a single monolithic web application, we maintain three specialized Angular applications that share common packages.

The Three Instances

Backend App (Port 4200)

Purpose: Production analysis and daily operations

Target Users: Operations managers, production supervisors

Key Features:

  • Real-time production monitoring
  • Daily operations dashboard
  • Manufacturing analytics
  • Quality control tracking
  • Resource management

Starting:

bash
yarn start-back  # Development
yarn start:back:prod  # Production build

Front App (Port 4201)

Purpose: User interactions and job tracking

Target Users: End users, operators, workers

Key Features:

  • Job tracking and management
  • Task assignment
  • Time tracking
  • User profile management
  • Personal dashboards

Starting:

bash
yarn start-front  # Development
yarn start:front:prod  # Production build

Dashboard App (Port 4202)

Purpose: External views and data visualization

Target Users: Executives, external stakeholders, clients

Key Features:

  • High-level visualizations
  • Executive dashboards
  • Performance metrics
  • External reporting
  • Public-facing views

Starting:

bash
yarn start-dashboard  # Development
yarn start:dash:prod  # Production build

Why Three Instances?

1. Separation of Concerns

Each instance serves a distinct purpose and user group:

  • Backend focuses on internal operations
  • Front focuses on user interactions
  • Dashboard focuses on visualization and reporting

This separation makes the codebase easier to understand and maintain.

2. Optimized Bundle Sizes

Each instance only includes what it needs:

  • No unused code from other instances
  • Smaller initial bundle sizes
  • Faster load times for each app

3. Independent Deployment

Each instance can be deployed independently:

  • Update Dashboard without affecting Backend
  • Deploy fixes to Front without rebuilding everything
  • Different deployment schedules for different instances

4. Targeted Optimization

Each instance can be optimized for its specific use case:

  • Backend optimized for real-time updates
  • Front optimized for user interactions
  • Dashboard optimized for data visualization

5. Security Boundaries

Different instances can have different security configurations:

  • Dashboard can be exposed externally
  • Backend restricted to internal network
  • Front with different authentication rules

Shared Package Strategy

All three instances share a common set of packages from @barcoding/*:

┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│   Backend   │  │    Front    │  │  Dashboard  │
│    :4200    │  │    :4201    │  │    :4202    │
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       │                │                │
       └────────────────┴────────────────┘

         ┌──────────────┴──────────────┐
         │                             │
    ┌────▼─────┐                 ┌────▼─────┐
    │   Core   │                 │   SDK    │
    │ Packages │                 │          │
    └──────────┘                 └──────────┘

Universal Packages (Used by All Three)

@barcoding/core

  • Forms and validators
  • Utility functions
  • ModuleStore and WidgetStore
  • Common models and interfaces

@barcoding/sdk

  • Auto-generated API client
  • 604 API services
  • API model interfaces

@barcoding/auth-core

  • Authentication services
  • Route guards
  • HTTP interceptors
  • AuthState (NGXS)

@barcoding/gridster-core

  • Widget system (CRITICAL)
  • Dashboard management
  • Widget configuration
  • Layout persistence

Instance-Specific Packages

@barcoding/backend-core

  • Used only by Backend app
  • Backend-specific utilities
  • Backend-specific services

@barcoding/frontend-core

  • Used only by Front app
  • Frontend-specific utilities
  • Frontend-specific services

@barcoding/dashboard-core

  • Used only by Dashboard app
  • Dashboard-specific utilities
  • Dashboard-specific services

Routing Structure

Each instance has its own routing configuration:

Backend App Routing

typescript
// projects/backend/src/app/app.routes.ts
export const routes: Routes = [
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  { path: 'dashboard', component: DashboardComponent },
  {
    path: 'modules/ncr',
    loadChildren: () => import('@barcoding/module-ncr')
      .then(m => m.ModuleNcrModule)
  },
  {
    path: 'modules/ticket',
    loadChildren: () => import('@barcoding/module-ticket')
      .then(m => m.ModuleTicketModule)
  }
];

Front App Routing

typescript
// projects/front/src/app/app.routes.ts
export const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: 'home', component: HomeComponent },
  {
    path: 'jobs',
    loadChildren: () => import('@barcoding/module-job')
      .then(m => m.ModuleJobModule)
  }
];

Dashboard App Routing

typescript
// projects/dashboard/src/app/app.routes.ts
export const routes: Routes = [
  { path: '', component: DashboardViewComponent },
  { path: 'widget/:id', component: WidgetDetailComponent }
];

Configuration Management

Each instance has its own environment configuration:

typescript
// projects/backend/src/environments/environment.ts
export const environment = {
  production: false,
  apiUrl: 'http://localhost:3001',
  appName: 'Backend'
};

// projects/front/src/environments/environment.ts
export const environment = {
  production: false,
  apiUrl: 'http://localhost:3001',
  appName: 'Front'
};

// projects/dashboard/src/environments/environment.ts
export const environment = {
  production: false,
  apiUrl: 'http://localhost:3001',
  appName: 'Dashboard'
};

Module Loading Strategy

Each instance decides which modules to load:

Backend App Modules

  • NCR module
  • Ticket module
  • HR module
  • IT module
  • Production modules

Front App Modules

  • Job module
  • Task module
  • Profile module
  • Time tracking module

Dashboard App Modules

  • Visualization modules
  • Report modules
  • Widget modules

Development Workflow

Working on All Instances

bash
# Terminal 1: Backend
cd web && yarn start-back

# Terminal 2: Front
cd web && yarn start-front

# Terminal 3: Dashboard
cd web && yarn start-dashboard

# Terminal 4: API
cd api/b3api && npm run start

Working on a Single Instance

bash
# Just start the instance you need
cd web && yarn start-back

# And the API
cd api/b3api && npm run start

Making Changes to Shared Packages

When you modify a shared package like @barcoding/core:

  1. Make your changes in web/projects/packages/@barcoding/core
  2. Rebuild the package: ng build @barcoding/core
  3. All three instances will pick up the changes (with hot reload in dev mode)

Building for Production

Each instance builds separately:

bash
# Build all three
yarn build

# Or build individually
yarn build:back:prod
yarn build:front:prod
yarn build:dash:prod

Output locations:

  • Backend: web/dist/backend/
  • Front: web/dist/front/
  • Dashboard: web/dist/dashboard/

Deployment Strategies

Option 1: Same Server, Different Paths

nginx
server {
  location /backend {
    alias /var/www/backend;
  }
  location /front {
    alias /var/www/front;
  }
  location /dashboard {
    alias /var/www/dashboard;
  }
}

Option 2: Different Subdomains

backend.example.com   → Backend app
front.example.com     → Front app
dashboard.example.com → Dashboard app

Option 3: Different Ports

example.com:4200 → Backend app
example.com:4201 → Front app
example.com:4202 → Dashboard app

Best Practices

1. Keep Shared Logic in Core Packages

If code is used by multiple instances, move it to a shared package:

typescript
// ❌ Bad: Duplicated in each instance
// projects/backend/src/app/utils/date.ts
// projects/front/src/app/utils/date.ts

// ✅ Good: In shared core package
// projects/packages/@barcoding/core/src/lib/utils/date.ts

2. Use Instance-Specific Cores for App Logic

Instance-specific logic goes in the appropriate core package:

typescript
// Backend-specific
import { BackendService } from '@barcoding/backend-core';

// Front-specific
import { FrontService } from '@barcoding/frontend-core';

3. Lazy Load Modules

Always lazy load feature modules to keep bundle sizes small:

typescript
{
  path: 'modules/ticket',
  loadChildren: () => import('@barcoding/module-ticket')
    .then(m => m.ModuleTicketModule)
}

4. Test Each Instance Independently

Run tests for each instance separately:

bash
yarn test:unit:backend-app
yarn test:unit:front-app
yarn test:unit:dashboard-app

Next Steps

Syneo/Barcoding Documentation