11 KiB
11 KiB
Plan: Consolidare ROA2WEB - Pragmatic Monolith
Branch:
feature/unified-app-pragmatic-monolithStatus: APROBAT - Ready for Implementation Efort estimat: ~2.5 zile
Context și Problema
Situația Curentă
- 2 aplicații frontend separate în IIS:
/roa2web/și/data-entry/ - Deploy greoi (2 build-uri, 2 configurații IIS separate)
- Componentele shared cauzează probleme CSS cross-app (text alb pe fundal alb)
- Nu există meniu unificat între aplicații
Obiectiv
- Un singur meniu cu ambele aplicații (Reports + Data Entry)
- Deploy simplificat (un build, un site IIS)
- Izolare între module (bug în Reports să nu afecteze DataEntry)
- URL-uri pe root:
/reports/*,/data-entry/*
Decizie Arhitecturală: Pragmatic Monolith
De ce NU Micro-frontends?
| Criteriu pentru MFE | ROA2WEB | Necesită MFE? |
|---|---|---|
| 20+ dezvoltatori | 1 dev | ❌ Nu |
| Deploy de multe ori/zi | Săptămânal | ❌ Nu |
| Milioane de utilizatori | 1-5 concurenți | ❌ Nu |
| Framework-uri diferite | Vue only | ❌ Nu |
Verdict: Module Federation / Single-SPA = OVERKILL
Abordare Aleasă: Monolith cu Mecanisme de Izolare
- Error Boundaries per modul (bug în Reports nu strică DataEntry)
- Lazy Loading (bundle-uri separate, încărcate la nevoie)
- Stores izolate per modul
- Feature flags pentru control
Blast radius cu protecții: 50-70% (aproape ca 2 apps separate!)
Arhitectura Finală
┌─────────────────────────────────────────────────────────────┐
│ ROA2WEB Unified SPA │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
│ │ Reports Module │ │ DataEntry Module │ │ Shared │ │
│ │ /reports/* │ │ /data-entry/* │ │ - Auth │ │
│ │ (lazy loaded) │ │ (lazy loaded) │ │ - Company │ │
│ │ │ │ │ │ - Period │ │
│ │ ErrorBoundary │ │ ErrorBoundary │ │ - Header │ │
│ └────────┬────────┘ └────────┬─────────┘ └──────────────┘ │
│ │ │ │
│ ┌────────┴────────────────────┴─────────┐ │
│ │ Vue Router + Global Error Handler │ │
│ └────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌─────────┴─────────┐
│ IIS Proxy │
└─────────┬─────────┘
┌───────────────┴───────────────┐
│ │
┌────────▼────────┐ ┌──────────▼────────┐
│ Reports Backend │ │ DataEntry Backend │
│ port 8001 │ │ port 8003 │
│ (Oracle RO) │ │ (SQLite + Oracle) │
└─────────────────┘ └───────────────────┘
Structura Proiect Unified
roa2web/
├── src/
│ ├── main.js
│ ├── App.vue # Meniu unificat
│ ├── router/index.js # Rute unificate cu lazy loading
│ │
│ ├── modules/
│ │ ├── reports/ # MODUL IZOLAT
│ │ │ ├── ReportsLayout.vue # Error boundary pentru modul
│ │ │ ├── views/
│ │ │ │ ├── DashboardView.vue
│ │ │ │ ├── InvoicesView.vue
│ │ │ │ ├── BankCashRegisterView.vue
│ │ │ │ ├── TrialBalanceView.vue
│ │ │ │ ├── TelegramView.vue
│ │ │ │ └── CacheStatsView.vue
│ │ │ ├── stores/
│ │ │ └── services/
│ │ │
│ │ └── data-entry/ # MODUL IZOLAT
│ │ ├── DataEntryLayout.vue
│ │ ├── views/
│ │ │ ├── ReceiptsListView.vue
│ │ │ └── ReceiptCreateView.vue
│ │ ├── stores/
│ │ └── services/
│ │
│ ├── shared/ # Shared între module
│ │ ├── components/ # AppHeader, SlideMenu, CompanySelector, etc.
│ │ ├── stores/ # Auth, Company, Period
│ │ └── styles/
│ │
│ └── config/
│ ├── menu.js # Configurație meniu unificat
│ └── features.js # Feature flags
│
├── vite.config.js
└── package.json
Pași Implementare
Faza 1: Setup Proiect (0.5 zile)
- Creează directorul
roa2web/ - Setup
package.jsoncu dependencies combinate din ambele apps - Setup
vite.config.jscu:- Dual proxy:
/api/reports/*→:8001,/api/data-entry/*→:8003 - Lazy loading chunks configuration
- Base path
/
- Dual proxy:
- Setup
main.jscu Pinia, PrimeVue, Router
Faza 2: Migrare Module (1 zi)
- Copiază views din
reports-app/frontend/src/views/→modules/reports/views/ - Copiază views din
data-entry-app/frontend/src/views/→modules/data-entry/views/ - Copiază stores specifice fiecărui modul
- Creează
ReportsLayout.vueșiDataEntryLayout.vuecu error boundaries - Setup router unificat:
const routes = [ { path: '/login', component: LoginView }, { path: '/reports', component: () => import('./modules/reports/ReportsLayout.vue'), children: [ { path: 'dashboard', component: () => import('./modules/reports/views/DashboardView.vue') }, { path: 'invoices', component: () => import('./modules/reports/views/InvoicesView.vue') }, // ... ] }, { path: '/data-entry', component: () => import('./modules/data-entry/DataEntryLayout.vue'), children: [ { path: '', component: () => import('./modules/data-entry/views/ReceiptsListView.vue') }, { path: 'create', component: () => import('./modules/data-entry/views/ReceiptCreateView.vue') }, ] }, { path: '/', redirect: '/reports/dashboard' } ]
Faza 3: Izolare și Resilience (0.5 zile)
- Implementează
ErrorBoundary.vue:<template> <div v-if="error" class="module-error"> <h3>⚠️ {{ moduleName }} a întâmpinat o eroare</h3> <p>{{ error.message }}</p> <button @click="retry">Reîncearcă</button> </div> <slot v-else /> </template> - Separă stores per modul (nu global)
- Adaugă feature flags în
config/features.js - Testează izolarea: introduce bug în Reports, verifică că DataEntry funcționează
Faza 4: Build & Deploy (0.5 zile)
- Verifică bundle splitting cu
npm run build:dist/assets/ ├── index-[hash].js # Shell + shared (~150KB) ├── reports-[hash].js # Reports module (~200KB) └── data-entry-[hash].js # DataEntry module (~100KB) - Update IIS web.config pentru SPA routing (toate rutele → index.html)
- Update deployment scripts pentru single app
- Test end-to-end pe server
- Deploy în producție
Meniu Unificat
// config/menu.js
export const menuSections = [
{
title: 'Rapoarte',
items: [
{ to: '/reports/dashboard', icon: 'pi pi-home', label: 'Dashboard' },
{ to: '/reports/invoices', icon: 'pi pi-file', label: 'Facturi' },
{ to: '/reports/bank-cash', icon: 'pi pi-money-bill', label: 'Casa și Banca' },
{ to: '/reports/trial-balance', icon: 'pi pi-calculator', label: 'Balanță' },
]
},
{
title: 'Introduceri Date',
items: [
{ to: '/data-entry', icon: 'pi pi-list', label: 'Lista Bonuri' },
{ to: '/data-entry/create', icon: 'pi pi-plus', label: 'Bon Nou' },
]
},
{
title: 'Sistem',
items: [
{ to: '/reports/telegram', icon: 'pi pi-telegram', label: 'Telegram' },
{ to: '/reports/cache-stats', icon: 'pi pi-chart-bar', label: 'Cache Stats' },
]
}
];
Fișiere de Referință
Fișiere Existente (sursă pentru copiere)
reports-app/frontend/src/views/*.vue- Views Reportsreports-app/frontend/src/stores/*.js- Stores Reportsreports-app/frontend/src/services/api.js- API service Reportsdata-entry-app/frontend/src/views/*.vue- Views DataEntrydata-entry-app/frontend/src/stores/*.js- Stores DataEntrydata-entry-app/frontend/src/services/api.js- API service DataEntryshared/frontend/components/- Componente shared (AppHeader, SlideMenu, etc.)shared/frontend/stores/- Stores shared (auth, companies, accountingPeriod)
Configurații de Referință
reports-app/frontend/vite.config.js- Pentru proxy și build configreports-app/frontend/package.json- Pentru dependenciesdata-entry-app/frontend/vite.config.js- Pentru proxy configdata-entry-app/frontend/package.json- Pentru dependencies
URL-uri Finale
| Path | Descriere |
|---|---|
/ |
Redirect la /reports/dashboard |
/login |
Pagină login |
/reports/dashboard |
Dashboard principal |
/reports/invoices |
Facturi |
/reports/bank-cash |
Casa și Banca |
/reports/trial-balance |
Balanță de Verificare |
/reports/telegram |
Telegram Bot |
/reports/cache-stats |
Statistici Cache |
/data-entry |
Lista Bonuri |
/data-entry/create |
Bon Nou |
/data-entry/:id |
Detalii Bon |
/data-entry/:id/edit |
Editare Bon |
Note Importante
- Backend-urile rămân separate - port 8001 (Reports) și port 8003 (DataEntry)
- IIS proxy routing - trebuie configurat pentru a ruta API calls corect
- Error Boundaries - critice pentru izolarea modulelor
- Lazy Loading - asigură bundle splitting corect
- Feature flags - permit dezactivarea unui modul fără redeploy
Handover Notes
Această implementare va înlocui cele 2 aplicații separate cu o aplicație unificată.
După implementare, directoarele reports-app/frontend/ și data-entry-app/frontend/
pot fi arhivate/șterse, păstrând doar unified-app/.
Backend-urile rămân neschimbate în reports-app/backend/ și data-entry-app/backend/.