Skip to content

Kiến trúc nội bộ

Bức tranh cho người sửa core: mỗi package build ra gì, phụ thuộc thế nào, app sản phẩm nạp chúng ra sao và một request đi qua những đâu. Góc nhìn của team sản phẩm: Kiến trúc.

Package và cách build

PackageBuild bằngSản phẩm publishAi dùng
@tasco/themeunbuilddist/ + styles/base.css (không qua build)ui, composables, layer, docs, mcp lúc sinh dữ liệu
@tasco/utilsunbuilddist/ui, composables, ai, layer
@tasco/composablesunbuild, nuxt/vue là externaldist/layer (auto-import)
@tasco/uivite build (lib) + vite-plugin-dts gộp d.ts + gen-component-meta.mjsdist/index.js, ui.css, index.d.ts, component-meta.jsonlayer, playground, docs, app sản phẩm, mcp lúc sinh dữ liệu
@tasco/aiunbuild, 4 entry: ., /server, /prompts, /evaldist/app sản phẩm (tuỳ chọn)
@tasco/nuxt-layer-basekhông bundle — build chỉ chạy nuxi prepare để có typeSource: nuxt.config.ts, app/, server/, tasco.d.tsapp sản phẩm extends
@tasco/clikhông buildbin/, templates/npx @tasco/cli
@tasco/mcptsx scripts/generate.ts rồi unbuilddist/, data/*.json, bin/AI tooling của dev
@tasco/eslint-config, @tasco/tsconfigkhông buildFile cấu hìnhmọi package và app

Turborepo chạy build, typecheck, test, dev sau ^build của package phụ thuộc — nên luôn chạy qua pnpm build hoặc pnpm turbo run … --filter=… thay vì gọi thẳng trong từng thư mục.

Phụ thuộc

                     ┌──────────┐   ┌──────────┐
                     │  theme   │   │  utils   │      TS thuần, không Vue/Nuxt
                     └────┬─────┘   └────┬─────┘
            ┌─────────────┼──────────────┼──────────────┐
            ▼             ▼              ▼              ▼
      ┌──────────┐  ┌────────────┐  ┌──────────┐   ┌─────────┐
      │    ui    │  │ composables│  │    ai    │   │   mcp   │ (theme, ui chỉ lúc sinh dữ liệu)
      │ + antdv  │  │  + Nuxt    │  │ + vue/h3 │   └─────────┘
      └────┬─────┘  └─────┬──────┘  └────┬─────┘
           └──────┬───────┘              │ tuỳ chọn
                  ▼                      ▼
          ┌────────────────┐       ┌──────────────┐
          │nuxt-layer-base │──────▶│ app sản phẩm │
          └────────────────┘extends└──────────────┘

Ràng buộc (vi phạm là từ chối MR — xem Quy ước code):

  • utilstheme không phụ thuộc Vue hay Nuxt — dùng lại được ở script, server, test thuần.
  • composables không phụ thuộc ui: composable nào cần antdv (useConfirm, useErrorHandler) đặt ở @tasco/ui.
  • Chỉ @tasco/ui, layer, Storybook preview và theme của docs được import ant-design-vue.
  • ai không nằm trong layer — app bật tính năng AI thì tự cài.
  • Tối đa hai tầng Nuxt layer: nuxt-layer-base → app.

App sản phẩm nạp framework thế nào

  1. App extends: ['@tasco/nuxt-layer-base']. Layer publish source: Nuxt đọc nuxt.config.ts, app/, server/ của layer như một phần của app.
  2. Layer khai build.transpile: ['@tasco/ui', '@tasco/composables'] — cài từ registry, hai package này nằm trong node_modules và sẽ bị Nitro externalize (lỗi ERR_MODULE_NOT_FOUND khi dev SSR) nếu không transpile.
  3. Composable vào app qua preset imports của layer; component C* qua plugin 01.ui (app.use(TascoUI)); type của component toàn cục và RouteMeta qua tasco.d.ts.
  4. @ant-design-vue/nuxt đăng ký a-* toàn cục cho layer; @nuxt/fonts tự host font.

Trong monorepo, playgrounddocs dùng workspace:* nên nạp thẳng thư mục package — một lỗi chỉ xuất hiện khi cài từ registry (thiếu files, thiếu transpile, thiếu @types/node) sẽ không lộ ra ở đây. Test của @tasco/cli (tests/template.test.mjs) khoá một số lỗi loại này.

Luồng runtime

Mở một trang

Trình duyệt ──GET /orders──▶ Nitro
                              routeRules '/**' ssr:false → trả HTML khung (lang="vi"), không render component
Trình duyệt tải JS
  plugin 01.ui          app.use(TascoUI)
  plugin 02.auth        GET /auth/session → useState('tasco:auth:user'); provide $tascoAuth
  plugin 03.permission  directive v-can
  plugin 04.error       errorHandler + vue:error → handleError; provide $tascoError
  middleware auth.global        chưa đăng nhập → /auth/login?redirect=/orders
  middleware permission.global  thiếu quyền trong meta → throw 403
  app.vue               ConfigProvider(theme, vi_VN) + <style id="tasco-vars"> + data-theme
  layouts/default.vue   CAppLayout + menu đã lọc quyền + NuxtErrorBoundary
  pages/orders.vue      useTable(fetcher) → useApi('/orders')

Gọi backend

useApi ──$fetch /api/orders──▶ server/api/[...].ts
                                getTascoSession(event)   giải mã cookie tasco_session
                                xoá authorization, cookie của client
                                gắn Bearer nếu token còn hạn
                                proxyRequest → apiProxyTarget/orders
         ◀── response nguyên trạng ──
lỗi HTTP ──▶ onResponseError → toAppError → throw AppError
           ──▶ trang tự bắt ($tascoError) hoặc errorHandler toàn cục → message / notification / chuyển trang đăng nhập

Đăng nhập

/auth/login (trang) → TascoLoginForm
  GET  /auth/clients         danh sách ứng dụng (IAM clients)
  POST /auth/otp/send        chỉ với Telegram → transactionId
  POST /auth/login           IAM login-v1 → token; userInfo (POST, header authorization không Bearer)
                             seal { user tối thiểu, accessToken, expiresAt } vào cookie httpOnly 8 giờ
  → useAuth cập nhật user → navigateTo(redirect đã lọc)
Mỗi lần app khởi động: GET /auth/session gọi lại userInfo để lấy roles/permissions mới

Cookie chỉ giữ token và định danh vì giới hạn 4KB; roles và permissions (có thể hàng trăm mục) luôn lấy tươi.

Theme

tokens.ts ──getAntdTheme(mode)──▶ ConfigProvider (token toàn cục + thuật toán sáng/tối của antdv)
          ──cssVars(mode)───────▶ biến --tasco-* trong <head> ──▶ style scoped của C*, CSS của app, base.css
base.css  ──────────────────────▶ phần antdv 4 bỏ qua theme.components: bảng, mật độ, chữ màu ở chế độ tối
useThemeMode (cookie tasco:theme) ──▶ đổi mode ──▶ app.vue tính lại cả hai nhánh

Dữ liệu sinh lúc build

Sinh raTừBằngDùng ở
packages/ui/dist/component-meta.jsonJSDoc, defineSlots trong *.vuevue-component-metaBảng API trong docs (<ApiTable>), docs-coverage, @tasco/mcp
packages/mcp/data/*.jsoncomponent-meta.json và re-export antdv của ui, token, docs/**/*.md (kèm demo), CLAUDE.mdscripts/generate.tsTool của @tasco/mcp
docs/.vitepress/theme/tokens.generated.csscssVarsTextgen-tokens.mjsBiến --tasco-* trong site docs
docs/reference/api/*.mdTSDoc của 4 package TSTypeDoc (gen-api.mjs)Khu Tra cứu
docs/reference/changelog.mdCHANGELOG.md các package, .changeset/*.mdgen-changelog.mjsTrang Changelog

Không sửa tay file sinh — sửa nguồn rồi build lại.

Muốn đổi … thì sửa ở đâu

MuốnSửaXem
Màu, font, mật độpackages/theme/src/tokens.ts, styles/base.cssĐổi design token
Thêm componentpackages/ui/src/components/ + đăng ký 3 chỗThêm component C*
Thêm composable, helperpackages/composables/src/, packages/utils/src/Thêm composable & util
Luồng đăng nhập, BFFpackages/nuxt-layer-base/server/Route BFF
Khoá cấu hình mớiruntimeConfig trong nuxt.config.ts của layer + trang tra cứuBiến môi trường
Thứ tự khởi động, xử lý lỗi toàn cụcpackages/nuxt-layer-base/app/plugins/API của layer
Dự án mới sinh ra gìpackages/cli/templates/app/CLI create-tasco-app

Ràng buộc từ công nghệ bên ngoài

Một số lựa chọn kiến trúc ở trên (CSR mặc định, style qua base.css, cookie chỉ giữ token) đến từ giới hạn của antdv, IAM và GitLab registry — danh sách gotcha: Nợ kỹ thuật › Khác với gotcha.