backend-admin/ — V4 Decision Breakdown

Laravel app tách riêng từ V3 — giờ bỏ hay giữ? Chi tiết quyết định per-domain, per-asset. Lúc trước tách vì HRM; V4 drop HRM → merge Laravel nhưng giữ SPA.

✂ DROP Laravel project · ✅ KEEP SPA as standalone

Mục lục

  1. Context — Lý do gốc tách backend-admin
  2. Decision — Merge Laravel + Extract SPA
  3. Laravel Side — 489 PHP files breakdown
  4. React SPA Side — 566 TSX files breakdown
  5. New Structure — 6 git repos độc lập
  6. Migration Path — Bước-by-bước
  7. Benefits — Consolidated architecture

1. Context — Tại sao V3 tách backend-admin

Hiểu lý do gốc để quyết định đúng cho V4.

🕰 V3 Reasons (2025-2026 Q1)

  • HRM domain (35 models) là lý do chính — cần module độc lập cho internal HR
  • Staff auth khác worker/employer — separate Sanctum guard
  • Deployment cycle khác — admin có ít user, release chậm hơn
  • React SPA UI khác với worker-facing web (Next.js)
  • DB riêng (viecxanh_admin) — cross-tenant aggregation

📋 V4 Changes (2026-Q2)

  • HRM bị DROP entirely — không phải V4 focus
  • Multi-tenant + role-based routing trong 1 app = isolation
  • Permission DB-driven — dynamic per-tenant
  • Unified JWT — 1 auth system
  • PostgreSQL unified — 1 DB, no cross-DB
Insight: Lý do gốc (HRM + separate domains + cross-DB) đều không còn áp dụng trong V4 architecture. Nhưng specialty admin UI (306 components + 164 hooks) vẫn có value — tách thành standalone frontend.

2. Decision — Merge Laravel + Extract SPA

2 hành động riêng biệt cho 2 phần của backend-admin.

📦 V3 backend-admin/

Laravel 12 + React SPA bundled

  • app/ — 489 PHP files (9 domains)
  • resources/js/src/ — 566 TSX files (257 pages)
  • routes/ — admin API + staff auth
  • database/ — 23 migrations
  • tests/ — 34 tests

🎯 V4 Target (2 places)

PHP merge + SPA standalone

  • backend/ — Merge PHP (non-HRM) vào unified Laravel
  • admin-spa/ — Standalone Vite 8 + React 19 app
  • Deploy admin.xanhvina.com.vn (nginx static)
  • Consume unified backend API
  • Giữ 75% SPA code, port 30% PHP code
Laravel Drop
60%
HRM domain removed
Laravel Adapt
30%
Merge vào unified
SPA Keep
75%
Design system + hooks
SPA Rewrite
5%
Dashboard per-tenant

3. Laravel PHP Side — 489 files breakdown

Per-domain quyết định: DROP hoàn toàn (HRM) hay MERGE vào unified backend V4.

Per-Domain Decision

V3 Domain% backend-adminActionV4 DestinationGhi chú
Hrm ~60% DROP 35 models, internal HR không phải V4 focus. Biggest removal.
Campaign ~8% MERGE backend/app/Domain/Recruitment Recruitment campaigns submodule
Crm ~10% SPLIT M3 + M4 + M8 (tách theo function) Lead flows: Recruitment, Worker, Supplier
Finance (staff view) ~8% MERGE backend/app/Domain/Payroll + Reconciliation Admin approval views
RecruitmentSupport ~5% MERGE backend/app/Domain/Supplier Recruiter tools
Trust ~4% MERGE backend/app/Domain/Reconciliation + Trust Layer Disputes + reputation
Attendance (admin) ~3% MERGE backend/app/Domain/Attendance Staff oversight routes
Infrastructure + Shared ~2% MERGE backend/app/Domain/Platform + IAM User sync → Integration Hub

Specific Components to DROP entirely

PublicApiClient proxy (~15 service methods)
V4 unified DB không cần proxy sang public API nữa.
UserSyncObserver
V4 có 1 user table duy nhất, không sync cần thiết.
DashboardAggregationService
Single-tenant aggregation → rewrite per-tenant trong V4 M11 AI Analytics.
config/staff-permissions.php
V4 permissions lưu trong DB table, dynamic per tenant.
Cross-DB MySQL connections
V4 chỉ 1 PostgreSQL connection — xóa config cross-DB.
HRM migrations (~15/23)
Không port sang V4 schema. Keep only non-HRM migrations (~8).

4. React SPA Side — 566 files breakdown

Extract thành admin-spa/ standalone Vite app. Giữ 75% code, adapt routing + auth.

Design System Assets (giữ toàn bộ)

UI Components 306

Tables, modals, forms, buttons, charts, inputs, dialogs...

Layout ~10

Sidebar, Header, Breadcrumb, TopBar, Footer

Utility Hooks ~20

useDebounce, useLocalStorage, useClickOutside...

Form Schemas ~50

Zod validation schemas + React Hook Form patterns

Chart configs Recharts

Theme, color palette, responsive wrappers

Rich Text TipTap

40+ extensions configured

DnD @dnd-kit

Drag-drop workflow patterns

Build Vite 8

Optimized pipeline + Tailwind v4

Pages Decision — 257 pages

Page GroupCountActionNotes
HRM pages~60DROPBiggest removal — không có V4 equivalent
Finance admin~40ADAPTPayroll approval + reconciliation views
Attendance admin~25ADAPTStaff oversight pages
Campaign~20ADAPTMove to V4 M3 campaigns
CRM~30ADAPTEmployer + worker management
Trust~15ADAPTDisputes + reputation
RecruitmentSupport~15KEEPMove to V4 M8 as-is
Infrastructure~10ADAPTTenant config, settings
Dashboard~20REWRITEPer-tenant analytics (hardest)
Shared layouts~22KEEPProfile, settings common

State + API Adaptations

Zustand Auth Store ADAPT

Thêm tenant context vào store shape:

// V3 auth store
{ user, token, isAuthenticated }

// V4 auth store
{ user, tenant, currentRole,
  token, isAuthenticated,
  switchTenant(slug) }

164 SWR Hooks ADAPT

Update API base URL + inject tenant header:

// V3
axios.get('/admin/api/workers')

// V4
axios.get('/v4/admin/workers',
  { headers: { 'X-Tenant-Slug': slug } })

Routing Adaptation

// V3 React Router v7 (single-tenant)
<Route path="/admin/hrm/employees" element={<Employees />} />
<Route path="/admin/campaigns" element={<Campaigns />} />

// V4 React Router v7 (multi-tenant)
<Route path="/t/:tenantSlug/admin/campaigns" element={<Campaigns />} />
<Route path="/t/:tenantSlug/admin/workers" element={<Workers />} />
<Route path="/system/tenants" element={<TenantManager />} />  // super admin

5. New Structure — 6 git repos độc lập

Không dùng Turborepo/pnpm workspaces. Mỗi app có git + deployment riêng.

# V4 parent folder (không phải monorepo tool) /Users/trungnguyen/Sites/viecxanh-v4/ ├── backend/ [own .git] Laravel 12 + PG 16 │ ├── app/Domain/ │ │ ├── Tenant/ M1 │ │ ├── Identity/ M2 │ │ ├── Recruitment/ M3 ← + Campaign, CRM leads │ │ ├── Worker/ M4 │ │ ├── Attendance/ M5 │ │ ├── Payroll/ M6 ← + Finance staff views │ │ ├── Platform/ M7 ← + Infrastructure shared │ │ ├── Supplier/ M8 ← + RecruitmentSupport │ │ ├── Factory/ M9 │ │ ├── Reconciliation/ M10 ← + Trust disputes │ │ ├── AI/ M11 │ │ └── WorkerApp/ M12 │ └── routes/ │ ├── admin.php ← staff routes (prev backend-admin) │ ├── worker.php │ ├── employer.php │ ├── mobile.php │ └── public.php │ ├── admin-spa/ [own .git] React 19 + Vite 8 │ ← Extracted from V3 backend-admin/resources/js/src/ │ ├── src/ │ │ ├── components/ 306 UI components (KEEP) │ │ ├── hooks/ 164 SWR hooks (ADAPT) │ │ ├── pages/ ~197 non-HRM pages (ADAPT) │ │ ├── layouts/ (KEEP) │ │ ├── store/ Zustand auth + app stores │ │ └── lib/ axios, utils │ ├── vite.config.ts │ └── package.json │ ├── web/ [own .git] Next.js 16 (public + portals) ├── worker-app/ [own .git] Expo 55 (VIỆC XANH 247) ├── business-app/ [own .git] Expo 55 (VIỆC XANH Business) ├── ai-service/ [own .git] NestJS 11 (renamed chat-service) └── docs/ [own .git] Documentation
Git model: 6 git repos độc lập. Mỗi repo có ownership rõ, branching + release tags riêng. Coordination qua PR references + shared docs repo. KHÔNG có tooling monorepo (Turborepo/Nx/pnpm workspaces) để giữ KISS.

Trade-offs của cách này

Dimension✅ Pros (Git độc lập)⚠ Cons
Simplicity Không cần học Turborepo/Nx. Git flow quen thuộc.
Ownership Rõ ràng per-team. PR focus 1 repo.
Deployment Decoupled. Deploy 1 app không ảnh hưởng app khác.
Shared code Phải copy (vd: TypeScript types) hoặc npm private package
Atomic refactor cross-app Khó hơn — cần PRs nhiều repos đồng bộ
Build cache Không shared cache (mỗi app build độc lập)
Migration Từng app migrate dần, rollback dễ
Kết luận: Với team 7-8 devs + 6 apps, git độc lập là sweet spot. Turborepo cần thiết khi 15+ devs + 20+ packages share heavily. Nếu sau này phát sinh nhu cầu shared package, có thể upgrade sang workspace tool mà không phải rewrite.

6. Migration Path — Bước-by-bước

Từ V3 backend-admin → V4 unified backend + standalone admin-spa.

Phase 1: Extract SPA (Tuần 1-2)

StepActionOutput
1Init new git repo admin-spa/Clean history
2Copy backend-admin/resources/js/src/admin-spa/src/Source code
3Copy backend-admin/package.json + vite config + tsconfigBuild pipeline
4Remove Laravel integration (laravel-vite-plugin)Pure Vite standalone
5Update API base URL env varsPoints to unified backend
6Setup nginx/CDN deployment cho admin.xanhvina.com.vnInfrastructure ready
7Verify build + dev server workGreen build

Phase 2: Port PHP domains (Tuần 3-8)

StepActionOutput
1Delete HRM domain code + migrationsClean slate
2Port Campaign → backend/app/Domain/RecruitmentMerged domain
3Port CRM (split into Recruitment/Worker/Supplier)3 targets
4Port Finance (admin views) → Payroll + ReconciliationMerged
5Port RecruitmentSupport → SupplierMerged M8
6Port Trust → Reconciliation + Trust LayerMerged M10 + cross-cutting
7Port Attendance admin viewsMerged M5
8Delete PublicApiClient + UserSyncObserverCleanup
9Port tests (non-HRM)Coverage maintained
10Delete backend-admin/ repo entirelyArchive + sunset

Phase 3: Adapt SPA (Tuần 3-8, parallel)

StepActionOutput
1Remove HRM pages (~60)Cleanup
2Update router: add /t/:tenantSlug prefixTenant routing
3Update Zustand auth store với tenant contextMulti-tenant aware
4Update 164 SWR hooks: new API URL + X-Tenant-Slug headerAPI integration
5Adapt Finance/Attendance/Campaign/CRM/Trust pages~130 pages adapted
6Rewrite Dashboard pages (per-tenant analytics)~20 pages rewritten
7Add TenantSwitcher component (super admin)Multi-tenant UX
8E2E tests với 2+ tenantsIsolation verified

7. Benefits — Consolidated Architecture

So sánh trước/sau khi merge backend-admin.

Operational Simplification

DimensionV3 (2 Laravel apps)V4 (Unified)Change
Laravel apps2 (backend + backend-admin)1 unified-50%
Databases2 MySQL1 PostgreSQL-50%
Cross-DB queries⚠ Attendance proxy reads work_records✅ NoneEliminated
Auth systems2 (staff Sanctum + worker/employer Sanctum)1 (unified JWT with scoped roles)-50%
PublicApiClient proxy20+ methods❌ RemovedDeleted
UserSyncObserverRequired (admin→public DB)❌ RemovedDeleted
Staff permissionsconfig file (hardcoded 358 perms)DB table (dynamic per tenant)Improved
Migration files71 split across 2 DBs92 unified+21 (V4 expanded)
Deployment pipelines2 separate (backend + admin)1 backend + 1 admin-spaCleaner separation
Frontend codebasesbackend-admin SPA embeddedadmin-spa standaloneDecoupled

Code Reuse Summary

PHP Laravel — ~30% reuse60% drop HRM + 10% rewrite
React SPA — ~75% reuse20% drop HRM pages + 5% rewrite dashboard
Design System — ~95% reuse306 components + 164 hooks
Tests — ~40% reuseNon-HRM tests port
Migrations — ~35% reuse8/23 non-HRM migrations

Summary

backend-admin Laravel project: DROP · backend-admin SPA: KEEP as admin-spa standalone

Laravel PHP → 30% merge vào unified backend · SPA React → 75% reuse extract thành standalone Vite app · Design system value preserved 95% · Deploy admin.xanhvina.com.vn không đổi.