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:
yarn start-back # Development
yarn start:back:prod # Production buildFront 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:
yarn start-front # Development
yarn start:front:prod # Production buildDashboard 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:
yarn start-dashboard # Development
yarn start:dash:prod # Production buildWhy 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
// 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
// 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
// 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:
// 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
# 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 startWorking on a Single Instance
# Just start the instance you need
cd web && yarn start-back
# And the API
cd api/b3api && npm run startMaking Changes to Shared Packages
When you modify a shared package like @barcoding/core:
- Make your changes in
web/projects/packages/@barcoding/core - Rebuild the package:
ng build @barcoding/core - All three instances will pick up the changes (with hot reload in dev mode)
Building for Production
Each instance builds separately:
# Build all three
yarn build
# Or build individually
yarn build:back:prod
yarn build:front:prod
yarn build:dash:prodOutput locations:
- Backend:
web/dist/backend/ - Front:
web/dist/front/ - Dashboard:
web/dist/dashboard/
Deployment Strategies
Option 1: Same Server, Different Paths
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 appOption 3: Different Ports
example.com:4200 → Backend app
example.com:4201 → Front app
example.com:4202 → Dashboard appBest Practices
1. Keep Shared Logic in Core Packages
If code is used by multiple instances, move it to a shared package:
// ❌ 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.ts2. Use Instance-Specific Cores for App Logic
Instance-specific logic goes in the appropriate core package:
// 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:
{
path: 'modules/ticket',
loadChildren: () => import('@barcoding/module-ticket')
.then(m => m.ModuleTicketModule)
}4. Test Each Instance Independently
Run tests for each instance separately:
yarn test:unit:backend-app
yarn test:unit:front-app
yarn test:unit:dashboard-appNext Steps
- Package Dependencies - Understand dependency hierarchy
- Creating Modules - Create new feature modules
- Widget System - Learn about widgets