286 lines
11 KiB
Markdown
286 lines
11 KiB
Markdown
# Plan: Consolidare ROA2WEB - Pragmatic Monolith
|
|
|
|
> **Branch**: `feature/unified-app-pragmatic-monolith`
|
|
> **Status**: 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)
|
|
1. Creează directorul `roa2web/`
|
|
2. Setup `package.json` cu dependencies combinate din ambele apps
|
|
3. Setup `vite.config.js` cu:
|
|
- Dual proxy: `/api/reports/*` → `:8001`, `/api/data-entry/*` → `:8003`
|
|
- Lazy loading chunks configuration
|
|
- Base path `/`
|
|
4. Setup `main.js` cu Pinia, PrimeVue, Router
|
|
|
|
### Faza 2: Migrare Module (1 zi)
|
|
1. Copiază views din `reports-app/frontend/src/views/` → `modules/reports/views/`
|
|
2. Copiază views din `data-entry-app/frontend/src/views/` → `modules/data-entry/views/`
|
|
3. Copiază stores specifice fiecărui modul
|
|
4. Creează `ReportsLayout.vue` și `DataEntryLayout.vue` cu error boundaries
|
|
5. Setup router unificat:
|
|
```javascript
|
|
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)
|
|
1. Implementează `ErrorBoundary.vue`:
|
|
```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>
|
|
```
|
|
2. Separă stores per modul (nu global)
|
|
3. Adaugă feature flags în `config/features.js`
|
|
4. Testează izolarea: introduce bug în Reports, verifică că DataEntry funcționează
|
|
|
|
### Faza 4: Build & Deploy (0.5 zile)
|
|
1. 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)
|
|
```
|
|
2. Update IIS web.config pentru SPA routing (toate rutele → index.html)
|
|
3. Update deployment scripts pentru single app
|
|
4. Test end-to-end pe server
|
|
5. Deploy în producție
|
|
|
|
---
|
|
|
|
## Meniu Unificat
|
|
|
|
```javascript
|
|
// 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 Reports
|
|
- `reports-app/frontend/src/stores/*.js` - Stores Reports
|
|
- `reports-app/frontend/src/services/api.js` - API service Reports
|
|
- `data-entry-app/frontend/src/views/*.vue` - Views DataEntry
|
|
- `data-entry-app/frontend/src/stores/*.js` - Stores DataEntry
|
|
- `data-entry-app/frontend/src/services/api.js` - API service DataEntry
|
|
- `shared/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 config
|
|
- `reports-app/frontend/package.json` - Pentru dependencies
|
|
- `data-entry-app/frontend/vite.config.js` - Pentru proxy config
|
|
- `data-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
|
|
|
|
1. **Backend-urile rămân separate** - port 8001 (Reports) și port 8003 (DataEntry)
|
|
2. **IIS proxy routing** - trebuie configurat pentru a ruta API calls corect
|
|
3. **Error Boundaries** - critice pentru izolarea modulelor
|
|
4. **Lazy Loading** - asigură bundle splitting corect
|
|
5. **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/`.
|