---
title: "專案設定 - Clean Architecture in TypeScript"
date: 2025-08-01T00:00:00+08:00
publishDate: 2025-08-01T00:00:00+08:00
lastmod: 2025-11-21T10:38:12+08:00
tags: ["TypeScript","Clean Architecture","架構","經驗","AI","Hono","Vite"]
series: "clean-architecture-in-ts"
toc: true
permalink: "https://blog.aotoki.me/posts/2025/08/01/clean-architecture-in-ts-project-setup/"
language: "zh-tw"
---



這一系列會預設在 Cloudflare 相容的環境下運行，在前端開發的部分會以 Vite 搭配 Hono 來使用，這幾個工具有很不錯的相容性，很適合在這次的情境中使用。

<!--more-->

## 初始化專案{#initialize}

首先，我們使用 Cloudflare 的 [Wrangler](https://developers.cloudflare.com/workers/wrangler/) 來初始化專案，建立一個以 Hello World 為樣板，選擇「Worker only」並且使用 TypeScript 的新專案。

```bash
npx wrangler init
```

大部分樣板的初始設定不會差異太大，但我們使用比較簡單的 Worker only 會比較容易繼續後續的處理。

接下來我們要把 Vite、Hono、Hono 的 Vite Dev Server 加入到專案裡面。

```bash
npm install -D vite @hono/vite-dev-server
npm install hono
```

接下來，先暫時將 `src/index.ts` 替換為以下內容，確保 Hono 運作正常。

```ts
import { Hono } from 'hono';

const app = new Hono();

app.get('/', (c) => c.text('Hello World!'));

export default {
	fetch: app.fetch,
} satisfies ExportedHandler<Env>;
```

因為 Worker only 樣板預設會對 `Hello World!` 的回傳進行測試，可以運行 `npm run test` 快速驗證修改。

接下來加入 `vite.config.mts` 設定使用 Vite 運行開發環境。

```ts
import devServer from "@hono/vite-dev-server";
import cloudflareAdapter from "@hono/vite-dev-server/cloudflare";
import { defineConfig, Plugin } from "vite";

export default defineConfig(() => {
	return {
		plugins: [
			devServer({
				entry: "./src/index.ts",
				adapter: cloudflareAdapter,
			}),
		] as Plugin[],
	};
});
```

Hono 已經幫我們準備好對應 Cloudflare 環境的 Adapter 因此會需要在 Vite 的 `devServer` 中額外設定，指定到我們要的檔案上。

最後更新 `package.ts` 將原本的 `wrangler dev` 替換為 `vite` 就可以用 `npm run dev` 看到使用 Vite 的版本。

## 前端設定{#frontend-setup}

我們要使用 Hono 同時來開發前端跟後端，這也是採用 Vite 的理由之一，因此我們還需要更近一步做設定，讓前後端都能夠被處理。

加入新的套件，用於處理前端的建置以及使用 TailwindCSS 作為本次前端的外觀設計。

```bash
npm install -D @hono/vite-build
npm install tailwindcss @tailwindcss/vite
```

接下來修改 `vite.config.mts` 為以下的狀態，根據 Hono 的文件，我們需要區分當下建置的是前端還是後端，並且給予不同的設定，不過在開發模式下只需要後端的即可，因此預設回傳後端的設定。

```ts
import build from "@hono/vite-build/cloudflare-workers";
import devServer from "@hono/vite-dev-server";
import cloudflareAdapter from "@hono/vite-dev-server/cloudflare";
import tailwindcss from "@tailwindcss/vite";
import { defineConfig, Plugin } from "vite";

export default defineConfig(({ mode }) => {
	if (mode == "client") {
		return {
			build: {
				rollupOptions: {
					input: ["./src/client.tsx"],
					output: {
						entryFileNames: "public/app.js",
						chunkFileNames: "public/assets/[name]-[hash].js",
						assetFileNames: "public/assets/[name].[ext]",
					},
				},
				emptyOutDir: false,
				copyPublicDir: false,
			},
			plugins: [tailwindcss()],
		};
	}

	return {
		plugins: [
			devServer({
				entry: "./src/index.tsx",
				adapter: cloudflareAdapter,
			}),
			build({
				entry: "./src/index.tsx",
			}),
			tailwindcss(),
		] as Plugin[]
	};
});
```

接著我們要修改 `tsconfig.json` 加入對應的設定，調整以下參數。

```json
{
	"compilerOptions": {
		"jsx": "react-jsx",
		"jsxImportSource": "hono/jsx",
	}
}
```

將 JSX 的來源指定為 `hono/jsx` 用來代替原生的 React 元件。

```json
{
	"compilerOptions": {
		"types": [
			"@cloudflare/workers-types/2023-07-01", "vite/client"
		],
	}
}
```

將 Vite 的型別定義加入到 `types` 中，新版的 Cloudflare Worker 可以不再依賴 `workers-types` 這邊可以考慮將其移除，短時間內不影響開發。

```json
{
	"include": ["worker-configuration.d.ts", "src/**/*.ts", "src/**/*.tsx"]
}
```

原本只會將 `.ts` 包含進來，因為使用了 JSX 所以需要把 `.tsx` 檔案也一起涵蓋進去。

接下來將原本的 `src/index.ts` 改為 `src/index.tsx` 加入空白的前端頁面。

```tsx
import { Hono } from 'hono';

const app = new Hono();

app.get("/", (c) =>
	c.html(
		<html lang="zh-TW">
			<head>
				<title>Clean Architecture in TypeScript</title>
				<meta charSet="utf-8" />
				<meta content="width=device-width, initial-scale=1" name="viewport" />
				{import.meta.env.PROD ? (
					<script type="module" src="/app.js" />
				) : (
					<script type="module" src="/src/client.tsx" />
				)}
			</head>
			<body>
				<div id="root" />
			</body>
		</html>,
	),
);

export default {
	fetch: app.fetch,
} satisfies ExportedHandler<Env>;
```

上述是 Hono 比較特殊的用法，我們可以直接在後端使用 JSX 語法撰寫 HTML 且能夠正常運作，如果專案比較單純的話，是一個不錯的方案。

加入新的 `src/client.tsx` 放入簡單的 `console.log("Hello World")` 我們就可以透過 `npm run dev` 看到畫面變成空白，並且在開發者工具的 Console 可以看到 Hello World 的訊息。

最後，更新 `wrangler.jsonc` 將以下這行註解取消，新專案還需要注意前一個設定尾端是否有 `,`

```json
{
	"assets": { "directory": "./public/", "binding": "ASSETS" },
}
```

這樣一來我們在開發跟部署就能夠都使用 Hono 來進行，其他像是 `vite-tsconfig-paths`、Pritter 等套件，則可以看需求安裝。

## 依賴注入{#dependency-injection}

因為我們選用 `tsyring` 作為依賴注入框架的關係，還需要額外啟用一些 TypeScript 的特性才可以使用。

先安裝依賴注入相關的套件。

```bash
npm install tsyringe reflect-metadata
```

在 `tsconfig.json` 啟用實驗性的功能

```json
{
	"compilerOptions": {
		"experimentalDecorators": true,
		"emitDecoratorMetadata": true
	}
}
```

最後在 `src/index.tsx` 第一行加入 Reflect Metadata 的引用。

```ts
import "reflect-metadata";

// ...
```

這個引用必須放在第一行，才不會在後續編譯後因為找不到引用的設定而無法正常運行專案。到此為止，我們將整個專案必要的設定都處理完畢，可以開始進入開發階段。

> 上述的專案設定大多是可以轉換為樣板的，如果認為經常會採用這樣的做法，以使用樣板機制為前提，撰寫成 AI 提示（Prompt）反而會降低成功率，AI 比較適合在針對細部微調的情境採用。
