EventHub Monorepo Migration Plan

0 comments0 reviews

Trạng thái: Architecture proposal — chưa thay đổi source code.
Cập nhật: 2026-10-15.
Phạm vi: chuyển EventHub hiện tại thành npm Workspaces gồm hai ứng dụng Nuxt độc lập: web và console.

Nguồn:


Kiến trúc đích được đề xuất:

  • Một repository, quản lý bằng npm Workspaces.
  • apps/web: EventHub public hiện tại, Nuxt 3, ssr: true, không còn route hoặc dependency của Builder.
  • apps/console: Event Builder nội bộ, ssr: false, đích cuối là Nuxt 4 và static output.
  • Mỗi app sở hữu package.json, nuxt.config.ts, source, test, Docker/hosting contract và artifact riêng.
  • Toàn workspace dùng một package-lock.json ở root.
  • apps/web tạo Nitro Node artifact tại apps/web/.output/.
  • apps/console chạy nuxi generate, tạo static artifact tại apps/console/.output/public/.
  • Hai app có pipeline, release version, deploy target và rollback độc lập.
  • Chỉ extract shared package khi cả Web và Console thực sự dùng và có ownership rõ ràng. Shared boundary đầu tiên có bằng chứng rõ nhất là event-page runtime, đặc biệt EventPageRenderer, model/validation và preview protocol.
  • Migration đầu tiên không nâng Web lên Nuxt 4. Builder nên được tách và kiểm chứng độc lập trên Nuxt 3 trước; việc nâng riêng Console lên Nuxt 4 là một phase có quality gate riêng.

#Quyết định kiến trúc

Chủ đềQuyết định đề xuất
Monorepo toolnpm Workspaces; chưa cần Nx hoặc Turborepo
App structureapps/web + apps/console
Dependency declarationMỗi app/package có package.json riêng
LockfileMột package-lock.json tại root
WebNuxt 3, SSR, Nitro Node server
ConsoleTách trên Nuxt 3 trước, sau đó nâng riêng lên Nuxt 4; CSR với ssr: false
Console outputStatic output bằng nuxt generate
Web outputNode artifact bằng nuxt build
Shared codeExtract theo nhu cầu thực tế, không tạo một package common tổng hợp ngay từ đầu
CI/CDHai pipeline release độc lập; shared package change có thể trigger cả hai

#Điều chỉnh so với yêu cầu ban đầu

package-lock.json riêng cho từng app mâu thuẫn với mô hình npm Workspaces chuẩn:

  • npm ci tại root dùng một dependency graph và một lockfile.
  • Mỗi app vẫn sở hữu dependency trực tiếp qua apps/<app>/package.json.
  • Root lockfile có thể chứa đồng thời Nuxt 3 của Web và Nuxt 4 của Console.
  • Nếu mỗi app bắt buộc có lockfile riêng và cài đặt riêng, nên dùng hai repository hoặc bỏ workspace relationship; không nên gọi đó là một npm workspace thống nhất.

#1.1 Background

EventHub hiện là một Nuxt application duy nhất:

  • Root app phục vụ public EventHub.
  • layers/collab-event và layers/promotion cung cấp public routes và event runtime.
  • layers/event-builder cung cấp route /builder.
  • Builder chỉ được đăng ký với các RUN_TYPE dev, dev2, qa2.
  • Web và Builder hiện dùng chung Nuxt config, dependency graph, plugin, middleware, runtime config và một .output.

Cấu trúc này làm hai product có cùng release boundary dù mục đích vận hành khác nhau:

  • Web là public product cần SSR, SEO, Nitro server và độ ổn định production cao.
  • Console là internal authoring tool, ưu tiên CSR, access control và release độc lập.

#1.2 Objective

Migration phải đạt các kết quả:

  1. Tạo một npm workspace cài đặt được bằng một lệnh từ root.
  2. Tách Web và Console thành hai Nuxt app độc lập.
  3. Mỗi app có config, dependency, build command và artifact riêng.
  4. Web giữ nguyên hành vi SSR và public routes, ngoại trừ Builder.
  5. Console sở hữu toàn bộ Event Builder.
  6. Hai app có thể build, deploy và rollback độc lập.
  7. Shared code có dependency direction rõ ràng, không import chéo giữa hai app.

#1.3 In scope

  • Thay đổi layout repository.
  • Tách package.json và Nuxt config theo app.
  • Tách artifact và pipeline.
  • Di chuyển Builder sang Console.
  • Extract event runtime thực sự được cả hai app dùng.
  • Cập nhật alias, auto-import, tests, Docker context và CI path rules.
  • Thiết lập Console ssr: false và static output.
  • Nâng riêng Console lên Nuxt 4 sau khi đã tách thành công.

#1.4 Out of scope

  • Không nâng Web lên Nuxt 4 trong migration này.
  • Không refactor business logic không liên quan.
  • Không đưa Nx/Turborepo vào giai đoạn đầu.
  • Không chuyển toàn bộ code hiện tại thành shared packages.
  • Không thay đổi API contract nếu không cần cho auth, CORS hoặc authorization của Console.
  • Không gộp release version của Web và Console.

#2.1 Cấu trúc hiện tại

event-hub/
├── nuxt.config.ts
├── package.json
├── package-lock.json
├── Dockerfile
├── pages/ layouts/ components/
├── composables/ stores/ services/ apis/ utils/
├── plugins/ middleware/ server/
├── configs/
│   └── runtime/run-type/
├── layers/
│   ├── collab-event/
│   ├── promotion/
│   └── event-builder/
├── public/ markup/ tests/
└── .output/                     # một artifact cho toàn ứng dụng

#2.2 Nuxt composition hiện tại

Root nuxt.config.ts đang:

  • Cấu hình ssr: true.
  • Luôn extend collab-event và promotion.
  • Chỉ extend event-builder khi RUN_TYPE thuộc dev, dev2, qa2.
  • Dùng chung modules, aliases, i18n, runtime config, Sentry, Vite, sitemap, robots và server config.
Root Nuxt app
├── collab-event layer
├── promotion layer
└── event-builder layer           # chỉ dev/dev2/qa2

Vì Builder là một layer của Web, nó hiện thừa hưởng:

  • Web app.vue và layout behavior.
  • Global middleware.
  • Auth/plugin của Web.
  • Public runtime config.
  • Root aliases.
  • Toàn bộ dependency của Web.
  • Cùng build artifact và release lifecycle.

#2.3 Build và artifact hiện tại

Các build script hiện tại đều chạy tại root:

npm run build:dev
npm run build:dev2
npm run build:qa
npm run build:qa2
npm run build:sandbox
npm run build:live

Tất cả gọi nuxt build với RUN_TYPE tương ứng và ghi:

.output/
├── public/
├── server/
│   └── index.mjs
└── nitro.json

Dockerfile hiện tại:

  • Dùng Node 20.11.1.
  • Copy root .output/.
  • Chạy node server/index.mjs.

Repository không chứa Jenkinsfile, .gitlab-ci.yml, Helm chart hoặc Kubernetes manifest. Pipeline Web thực tế nằm ngoài repository và phải được Infra cung cấp trước khi cutover.

#2.4 Coupling cần xử lý

Research hiện tại ghi nhận:

  • layers/promotion: 79 file import root qua @/.
  • layers/collab-event: 14 file import root qua @/.
  • layers/event-builder: 13 file import root qua @/.
  • Builder import @promotion/entities/event-page với mật độ rất cao.
  • Builder dùng @promotion/shared/lib, @promotion/shared/ui.
  • Codegen của Builder sinh import dành cho Web.
  • Preview iframe hiện trỏ tới public promotion route và giả định same-origin.

Các dependency Builder cần phân loại:

Event Builder
├── Builder-only
│   ├── canvas
│   ├── inspector
│   ├── workflow
│   ├── persistence UI
│   └── code generation UI
├── Shared event runtime
│   ├── event-page model/validation
│   ├── EventPageRenderer
│   ├── preview messaging
│   ├── stage policy
│   └── event config helpers
└── Platform dependencies
    ├── useRequest / API client
    ├── auth
    ├── runtime config
    ├── alert/tooltip
    └── shared constants/types

#2.5 Current deployment view

#2.6 Những điểm phải giữ nguyên cho Web

  • Public route list, trừ Builder.
  • SSR behavior và hydration.
  • Locale redirect.
  • Runtime config theo RUN_TYPE.
  • Sentry behavior.
  • .well-known assets.
  • API/auth behavior.
  • Nitro start command.
  • Existing environment matrix.
  • Markup mode nếu team vẫn sử dụng.

#3.1 Directory structure

event-hub/
├── apps/
│   ├── web/                                  # @event-hub/web
│   │   ├── package.json
│   │   ├── nuxt.config.ts                    # Nuxt 3, ssr:true
│   │   ├── app.vue
│   │   ├── error.vue
│   │   ├── configs/
│   │   ├── pages/
│   │   ├── layouts/
│   │   ├── components/
│   │   ├── composables/
│   │   ├── stores/
│   │   ├── services/
│   │   ├── apis/
│   │   ├── plugins/
│   │   ├── middleware/
│   │   ├── server/
│   │   ├── public/
│   │   ├── markup/
│   │   ├── layers/
│   │   │   ├── promotion/                    # Web-only local layer
│   │   │   └── collab-event/                 # Web-only local layer
│   │   ├── tests/
│   │   └── Dockerfile                        # Node/Nitro image
│   │
│   └── console/                              # @event-hub/console
│       ├── package.json
│       ├── nuxt.config.ts                    # ssr:false
│       ├── app.vue
│       ├── configs/
│       ├── pages/
│       │   ├── index.vue
│       │   ├── builder/index.vue
│       │   └── preview/[slug].vue
│       ├── layouts/
│       ├── plugins/
│       ├── middleware/
│       ├── layers/
│       │   └── event-builder/                # Console-only local layer
│       ├── public/
│       ├── tests/
│       ├── deploy/
│       │   └── nginx.conf
│       └── Dockerfile                        # static image, nếu deploy bằng container
│
├── packages/
│   ├── nuxt-layers/
│   │   └── event-runtime/                    # chỉ code thực sự dùng bởi cả hai app
│   │       ├── package.json
│   │       ├── nuxt.config.ts
│   │       ├── entities/event-page/
│   │       ├── widgets/event-renderer/
│   │       ├── shared/lib/
│   │       ├── shared/ui/
│   │       └── config/events/
│   ├── event-contract/                       # optional: TS thuần sau khi boundary ổn định
│   │   ├── package.json
│   │   └── src/
│   └── configs/                              # optional: eslint/tsconfig/tailwind khi có lợi ích rõ
│
├── scripts/
│   └── ci/
├── docs/
├── patches/
├── package.json                              # workspace orchestration
├── package-lock.json                         # lockfile duy nhất
├── .npmrc
├── .nvmrc
└── .gitlab-ci.yml                            # nếu Console dùng GitLab CI

#3.2 Ownership boundary

apps/web

Sở hữu:

  • Public pages và layouts.
  • promotion và collab-event.
  • Web-only plugins: launcher, mobile SDK, ads, SEO, sitemap, robots.
  • Nitro server, .well-known, Sentry server.
  • Public Web Docker image và deployment.

Không được:

  • Import source từ apps/console.
  • Đăng ký event-builder.
  • Chứa route /builder.

apps/console

Sở hữu:

  • Builder page, layout, canvas, inspector, workflow và persistence UX.
  • Console auth guard và access policy.
  • Console runtime config.
  • Same-origin preview shell.
  • Static hosting config và Console release.

Không được:

  • Import source từ apps/web.
  • Kế thừa Web app hoặc Web-only plugins.
  • Dựa vào route guard phía client như lớp authorization duy nhất.

packages/nuxt-layers/event-runtime

Chỉ chứa code có ít nhất hai consumer:

  • EventPageRenderer.
  • Event page model, validation và stage policy.
  • Preview messaging contract.
  • Helper/UI primitive gắn trực tiếp với event runtime.
  • Event config dùng cho render/preview.

Không dùng @/ hoặc ~/, vì các alias này trỏ về app consumer. Import nội bộ phải dùng package name hoặc alias riêng của layer.

packages/event-contract

Chỉ tạo khi model thuần đã tách khỏi Nuxt/Vue:

  • TypeScript types.
  • Validation.
  • Factory.
  • Stage policy.
  • Preview message types.

Không chứa component, composable, useRuntimeConfig hoặc Nuxt auto-import.

#3.3 Vì sao chọn cấu trúc này

So với giữ Builder trong Web

  • Loại Builder khỏi public artifact.
  • Không buộc Console theo release cadence của Web.
  • Giảm blast radius khi thay đổi authoring tool.
  • Có thể nâng Console lên Nuxt 4 mà không chạm Web.
  • Rõ trách nhiệm vận hành và access control.

So với tách thành repository riêng

  • Có thể thay đổi event contract và hai consumer trong cùng một MR.
  • Một lockfile giúp dependency graph có thể tái tạo.
  • Dùng lại code runtime mà không phải publish package lên registry.
  • CI có thể xác định app bị ảnh hưởng từ dependency graph.
  • Dễ thực hiện migration từng phase hơn.

Vì sao chưa cần Nx/Turborepo

  • Chỉ có hai app và một số ít shared packages.
  • npm Workspaces đã giải quyết install và package linking.
  • Build của mỗi app đã độc lập.
  • Path-based CI đủ cho giai đoạn đầu.
  • Có thể bổ sung task runner sau nếu build time, caching hoặc graph orchestration trở thành vấn đề thực tế.

#3.4 Dependency direction

Quy tắc:

  • web và console không import lẫn nhau.
  • Package shared không import app.
  • Builder là Console-only, nên không cần biến thành shared package.
  • Promotion và Collab là Web-only, nên ở trong apps/web/layers.
  • Dependency phải được khai báo trong package.json của đúng consumer; không dựa vào npm hoisting.

#3.5 Nuxt version strategy

Đích cuối:

  • Web: giữ Nuxt 3.
  • Console: Nuxt 4.

Thứ tự migration được khuyến nghị:

  1. Tạo Console tối thiểu và tách Builder trên cùng Nuxt major với Web.
  2. Chứng minh Builder build/chạy độc lập.
  3. Cắt Builder khỏi Web.
  4. Nâng riêng Console lên Nuxt 4.

Lý do không nâng Console đồng thời với lần move Builder:

  • Nếu build hỏng, có thể phân biệt lỗi do move source với lỗi do Nuxt major.
  • Shared layer có thời gian để loại bỏ @/, auto-import ngầm và Nuxt 3-only behavior.
  • Rollback đơn giản hơn.

Nếu team quyết định tạo Console trực tiếp bằng Nuxt 4, phải có một compatibility spike trước khi migrate Builder và không được tính spike là bằng chứng production readiness.

#3.6 Lockfile và version policy

Root workspace:

{
  “name”: “event-hub-monorepo”,
  “private”: true,
  “workspaces”: [
    “apps/*“,
    “packages/*“,
    “packages/nuxt-layers/*“,
    “packages/configs/*”
  ]
}

Quy tắc:

  • Một package-lock.json ở root.
  • npm ci chỉ chạy từ root.
  • Mỗi app pin exact Nuxt major/version của chính nó trong app package.json.
  • Không dùng root overrides để ép Nuxt 3 và Nuxt 4 thành một version.
  • Shared package không khai báo Nuxt/Vue là runtime dependency nếu không cần; dùng peer dependency có range đã được kiểm thử.
  • Sau khi Console lên Nuxt 4, CI phải build shared runtime với cả hai app.
  • patch-package của Nuxt 3 cần một CI assertion riêng vì npm có thể hoist/nest khác sau khi thêm Nuxt 4.

#4.1 Root commands

Root package.json chỉ orchestration:

{
  “scripts”: {
    “dev:web”: “npm run serve --workspace=@event-hub/web”,
    “dev:console”: “npm run serve --workspace=@event-hub/console”,

    “build:web:dev”: “npm run build:dev --workspace=@event-hub/web”,
    “build:web:qa”: “npm run build:qa --workspace=@event-hub/web”,
    “build:web:sandbox”: “npm run build:sandbox --workspace=@event-hub/web”,
    “build:web:live”: “npm run build:live --workspace=@event-hub/web”,

    “build:console:dev”: “npm run build:dev --workspace=@event-hub/console”,
    “build:console:qa”: “npm run build:qa --workspace=@event-hub/console”,
    “build:console:live”: “npm run build:live --workspace=@event-hub/console”,

    “build:all:dev”: “npm run build:web:dev && npm run build:console:dev”,
    “lint”: “npm run lint --workspaces --if-present”,
    “typecheck”: “npm run typecheck --workspaces --if-present”,
    “test”: “npm run test --workspaces --if-present”
  }
}

build:all:* chỉ dùng cho local validation hoặc quality gate. Release pipeline phải gọi build của từng app riêng.

#4.2 Web build

apps/web/package.json giữ script hiện tại:

{
  “name”: “@event-hub/web”,
  “private”: true,
  “scripts”: {
    “serve”: “cross-env RUN_TYPE=dev nuxt dev”,
    “build:dev”: “cross-env NODE_OPTIONS=--max-old-space-size=4096 BUILD_VERSION=$BUILD_VERSION RUN_TYPE=dev SENTRY=off nuxt build”,
    “build:qa”: “cross-env NODE_OPTIONS=--max-old-space-size=4096 BUILD_VERSION=$BUILD_VERSION RUN_TYPE=qa SENTRY=off nuxt build”,
    “build:sandbox”: “cross-env NODE_OPTIONS=--max-old-space-size=4096 BUILD_VERSION=$BUILD_VERSION RUN_TYPE=sandbox SENTRY=on nuxt build”,
    “build:live”: “cross-env NODE_OPTIONS=--max-old-space-size=4096 BUILD_VERSION=$BUILD_VERSION RUN_TYPE=live SENTRY=on nuxt build”
  }
}

Build:

npm ci
npm run build:web:live

Artifact:

apps/web/.output/
├── nitro.json
├── public/
└── server/
    └── index.mjs

Runtime:

node apps/web/.output/server/index.mjs

#4.3 Console build

apps/console/package.json:

{
  “name”: “@event-hub/console”,
  “private”: true,
  “scripts”: {
    “serve”: “cross-env RUN_TYPE=dev nuxt dev”,
    “build:dev”: “cross-env RUN_TYPE=dev nuxi generate”,
    “build:qa”: “cross-env RUN_TYPE=qa nuxi generate”,
    “build:live”: “cross-env RUN_TYPE=live nuxi generate”
  }
}

apps/console/nuxt.config.ts:

export default defineNuxtConfig({
  ssr: false,
  extends: [‘./layers/event-builder’, ‘@event-hub/event-runtime’],
  app: {
    head: {
      meta: [{ name: ‘robots’, content: ‘noindex, nofollow’ }]
    }
  }
});

Build:

npm ci
npm run build:console:dev

Artifact:

apps/console/.output/public/
├── index.html
├── 200.html
├── 404.html
├── _nuxt/
└── assets copied from public/

Console không có Node/Nitro runtime sau deploy. Static server phải có SPA fallback:

location / {
  try_files $uri $uri/ /200.html;
}

#4.4 Hai instance, hai artifact

Mỗi artifact phải có:

  • App name.
  • Commit SHA.
  • Build environment.
  • Build timestamp.
  • Version/release tag.
  • Sentry release nếu áp dụng.

#4.5 Temporary Jenkins compatibility

Trong phase đầu, có thể giữ nguyên Jenkins command hiện tại:

npm run build:live

Root script proxy sang Web rồi copy artifact về root:

{
  “scripts”: {
    “build:live”: “npm run build:live --workspace=@event-hub/web && node scripts/ci/sync-web-output.mjs”
  }
}

sync-web-output.mjs phải copy bảo toàn symlink:

apps/web/.output/ → ./.output/

Sau một release cycle ổn định, Jenkins chuyển sang native:

npm ci
npm run build:live --workspace=@event-hub/web
docker build -f apps/web/Dockerfile apps/web

Khi đó bỏ root .output, compatibility script và root Dockerfile.


#Phase 0 — Baseline và quyết định

Mục tiêu: có bằng chứng trước khi move source.

Actions:

  1. Lấy Jenkins job/pipeline thật từ Infra.
  2. Ghi lại install command, build command, Docker context, runtime env và probe.
  3. Build baseline cho dev, qa, sandbox, live.
  4. Lưu route list, nitro.json, bundle/chunk list và kích thước artifact.
  5. Chốt Console domain, auth, CORS, preview và static hosting.
  6. Chốt Nuxt 4 timing cho Console.
  7. Commit các script verify cần dùng; hiện scripts/ đang bị .gitignore.

Exit criteria:

  • Baseline có thể chạy lại.
  • Infra xác nhận pipeline hiện tại.
  • Các quyết định security/runtime của Console có owner.

#Phase 1 — Setup npm workspace

Mục tiêu: đổi repository layout nhưng chưa migrate Builder behavior.

Actions:

  1. Thêm root workspaces.
  2. Tạo apps/web/package.json.
  3. Move root Nuxt app vào apps/web.
  4. Move promotion và collab-event vào apps/web/layers.
  5. Giữ Builder tạm thời ở vị trí có thể được Web consume trong transition.
  6. Tạo apps/console tối thiểu, chưa có Builder.
  7. Tạo lại root package-lock.json.
  8. Pin dependency để tránh nâng version ngoài ý muốn.
  9. Thêm root build proxy để Jenkins chưa phải đổi.

Exit criteria:

  • npm ci tại root thành công.
  • Web artifact parity với baseline.
  • Console tối thiểu build static độc lập.
  • Jenkins Web chạy mà không đổi deployment behavior.

Rollback:

  • Revert workspace/move MRs.
  • Jenkins tiếp tục dùng layout cũ.

#Phase 2 — Independent build validation

Mục tiêu: chứng minh hai app có build boundary thật.

Actions:

  1. Chạy Web build cho toàn bộ RUN_TYPE.
  2. Chạy Console build từ clean install.
  3. Kiểm tra output path không đè nhau.
  4. Kiểm tra dependency của Console không xuất hiện trong Web artifact.
  5. Kiểm tra build Web không cần source Console.
  6. Kiểm tra Docker build context mới.
  7. Thêm quality gate cho package boundaries.

Exit criteria:

  • Hai build có thể chạy riêng và chạy đồng thời.
  • Xóa .output của app này không ảnh hưởng app kia.
  • Web SSR smoke test pass.
  • Console static hosting smoke test pass với deep-link fallback.

#Phase 3 — Extract shared event runtime

Mục tiêu: tạo shared boundary tối thiểu đủ để Builder và Web cùng render event.

Actions:

  1. Tạo packages/nuxt-layers/event-runtime.
  2. Move EventPageRenderer.
  3. Move event-page model, validation, stage policy và preview contract cần cho cả hai.
  4. Đổi import khỏi @promotion/* sang package name.
  5. Loại @/ và ~/ khỏi shared package.
  6. Chỉ tạo event-contract nếu phần model đã thực sự framework-agnostic.
  7. Giữ Web behavior không đổi.

Exit criteria:

  • Web render public event qua shared runtime.
  • Console preview render cùng runtime.
  • Shared package không import app.
  • Shared package build được trong cả Web và Console.

#Phase 4 — Migrate Builder sang Console

Mục tiêu: Console sở hữu Builder nhưng Web vẫn có fallback tạm thời nếu cần.

Actions:

  1. Move layers/event-builder vào apps/console/layers/event-builder.
  2. Tách Console plugins, config và auth.
  3. Sửa alias và auto-import.
  4. Di chuyển Builder tests.
  5. Thêm same-origin preview route trong Console.
  6. Cập nhật codegen contract.
  7. Deploy Console dev/QA.

Exit criteria:

  • Builder load/list/create/save/promote theo acceptance flow.
  • Preview desktop/mobile hoạt động.
  • Không import source từ apps/web.
  • Console artifact chỉ là static output.

#Phase 5 — Upgrade Console lên Nuxt 4

Mục tiêu: nâng riêng Console, không thay đổi Web.

Actions:

  1. Pin Nuxt 4 và dependency tương thích trong Console.
  2. Cập nhật directory semantics hoặc srcDir tường minh.
  3. Kiểm tra shared event runtime trên Nuxt 3 và Nuxt 4.
  4. Sửa API deprecated và auto-import khác biệt.
  5. Regenerate root lockfile.
  6. Chạy full Builder acceptance test.

Exit criteria:

  • Web vẫn build bằng Nuxt 3.
  • Console build static bằng Nuxt 4.
  • Root install chứa đúng hai dependency trees.
  • Shared runtime pass test trên cả hai app.

Rollback:

  • Revert riêng Console dependency/config về Nuxt 3.
  • Không rollback Web.

#Phase 6 — Cut Builder khỏi Web

Actions:

  1. Xóa SUPPORTED_BUILDER_ENABLED_RUN_TYPES.
  2. Xóa Builder khỏi Web extends.
  3. Xóa alias và dependency Builder khỏi Web.
  4. Assert Web route manifest không có /builder.
  5. Thông báo URL Console mới cho Product/QA/Content.

Exit criteria:

  • Web artifact không còn Builder source/chunk/route.
  • Console đã được dùng ổn định ít nhất một sprint hoặc release window đã thống nhất.

#Phase 7 — Native CI/CD và cleanup

Actions:

  1. Jenkins build trực tiếp apps/web.
  2. Console dùng pipeline riêng.
  3. Xóa compatibility artifact copy.
  4. Thêm affected path rules.
  5. Cập nhật AGENTS/CLAUDE/rules/docs theo layout mới.
  6. Thiết lập release tag riêng, ví dụ [email protected], [email protected].

Exit criteria:

  • Hai pipeline release độc lập.
  • Mỗi app rollback độc lập.
  • Shared change trigger đúng consumer.

#6.1 Source code impact

Mức ảnh hưởng: cao trong migration, thấp sau khi ổn định.

Thay đổi:

  • Hầu hết root path chuyển xuống apps/web.
  • Relative path trong Nuxt config fragments phải cập nhật.
  • process.cwd() trong build hooks sẽ thay đổi thành app directory.
  • Alias @/ trong layers không còn an toàn.
  • Tests, fixtures và generated output path phải cập nhật.
  • i18n, OpenAPI generation và .well-known copy phải chạy với đúng app cwd.

Giảm thiểu:

  • Move bằng MR riêng với sửa behavior.
  • Baseline artifact trước move.
  • Build mọi RUN_TYPE.
  • Cấm cross-app import bằng ESLint.

#6.2 Dependency impact

Mức ảnh hưởng: cao.

Rủi ro:

  • Regenerate root lockfile có thể resolve version mới.
  • Nuxt 3 và Nuxt 4 có thể được hoist/nest khác dự kiến.
  • patch-package có thể patch sai Nuxt location.
  • Phantom dependency có thể chạy local nhưng fail trong CI.
  • Shared Vue component có thể gặp khác biệt compiler/runtime.

Giảm thiểu:

  • Pin exact version trong từng app khi migration.
  • So sánh npm ls --all trước/sau.
  • CI clean install.
  • Mỗi package khai báo dependency trực tiếp.
  • Test shared runtime bằng cả hai build.

#6.3 Web runtime impact

Mức ảnh hưởng: trung bình, nhưng blast radius production cao.

Các vùng nhạy cảm:

  • SSR và hydration.
  • Locale redirect.
  • Server plugins.
  • Auth cookie.
  • Sentry.
  • Sitemap/robots.
  • .well-known.
  • Healthcheck.

Lưu ý hiện tại:

  • Có route /api/healthz.
  • Probe /healthcheck/_check.html cần Infra xác nhận vì không phải explicit route trong source.
  • RUN_TYPE, SENTRY, BUILD_VERSION phải được bảo toàn.

#6.4 Console runtime impact

Mức ảnh hưởng: cao, vì từ một layer thành một product.

Static Console không có Nitro server, do đó:

  • Không thể tự tạo BFF/proxy bằng server routes.
  • Runtime config public bị bake tại build time.
  • Mỗi environment cần artifact riêng nếu endpoint khác nhau.
  • Healthcheck do Nginx/ingress/static host cung cấp.
  • Auth/CORS phải giải quyết ở API gateway hoặc edge.

#6.5 Preview impact

Preview hiện giả định same-origin với public promotion route.

Khuyến nghị:

  • Console có route /preview/[slug].
  • Route này render EventPageRenderer từ shared runtime.
  • Iframe và parent cùng Console origin.
  • Thêm action “Open on Web” để QA kiểm tra public shell thật.

Nếu bắt buộc iframe Web thật:

  • Phải cấu hình target origin allowlist.
  • Web phải cho phép Console origin qua CSP frame-ancestors.
  • postMessage phải validate origin và source.
  • Preview bị phụ thuộc vào version Web đang deploy.

#6.6 Security impact

“Internal” không thay thế authentication và authorization.

Phải xác nhận:

  • Console domain có nằm dưới .onstove.com hay không.
  • Cookie của seed-core có đọc được trên domain Console hay không.
  • Backend có role check cho API ghi event page hay không.
  • CORS allowlist có Console origin hay không.
  • Có VPN, corporate network, IP allowlist hoặc SSO ở edge hay không.
  • Static bundle không chứa secret.

Nếu Console ở domain không thuộc .onstove.com, cookie .onstove.com không thể được browser gửi như same-site app cookie. Cần corporate SSO, edge proxy hoặc backend admin API phù hợp; không thể sửa bằng Nuxt route guard.


#7.1 Web pipeline

Giai đoạn compatibility

  • Install vẫn là npm ci tại root.
  • Command Jenkins hiện tại có thể giữ nguyên qua root proxy.
  • Root .output được tạo tạm từ apps/web/.output.
  • Root Dockerfile chưa đổi.

Giai đoạn native

  • Build command:
npm ci
npm run build:live --workspace=@event-hub/web
  • Docker command:
docker build -f apps/web/Dockerfile apps/web
  • Artifact path:
apps/web/.output/

Infra phải cập nhật:

  • Jenkins workspace path.
  • Docker build context.
  • Artifact collection path.
  • Sentry sourcemap path.
  • Healthcheck route nếu cần.
  • Cache key dựa trên root package-lock.json.

#7.2 Console pipeline

Build command:

npm ci
npm run build:live --workspace=@event-hub/console

Artifact path:

apps/console/.output/public/

#7.3 Pipeline independence

Path rule ban đầu:

apps/web/**                         → Web
apps/console/**                     → Console
packages/nuxt-layers/event-runtime/** → Web + Console
packages/event-contract/**          → Web + Console
package.json                        → Web + Console
package-lock.json                   → Web + Console
patches/**                          → app liên quan; mặc định cả hai nếu chưa xác định

Release build sandbox/live không nên bị skip chỉ vì path filter; khi được trigger release, app đó phải build đầy đủ.

#7.4 Artifact contract

ContractWebConsole
Build commandnuxt buildnuxi generate
RenderSSRCSR
Outputapps/web/.outputapps/console/.output/public
RuntimeNode/NitroStatic host/Nginx
HealthNitro routeEdge/Nginx route
ConfigBuild-time + server runtime hiện tạiBuild-time public config
RollbackPrevious Node imagePrevious static image/bundle