iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0
Software Development

我獨自開發 - 30天進化之路,掌握 Laravel + Nuxt系列 第 27

D27 - 系統優化之路:環境部署、後端重構與前端元件化

  • 分享至 

  • xImage
  •  

哈囉,大家好!經過前面二十多天的努力,我們的個人財務管理系統已經完成了主要的功能開發。
在最後的幾天裡,我想和大家一起來優化我們的系統,讓它不僅能夠運行,還能夠更加穩定、安全、易於維護。

今天,我們將從三個面向進行優化:

  1. 系統環境的結構性調整:探討如何將我們的 Docker 開發環境部署到生產環境,並確保其運行與安全性。
  2. 後端的重構與安全性:優化 API 的存取權限、日誌記錄,以及程式碼的重構方向。
  3. 前端介面的可複用性:探討如何在前端進行元件化設計,讓資料的取得更加結構化。
    讓我們開始今天的內容吧!

一、系統環境的結構性調整

1. 開發環境與生產環境的差異

在開發過程中,我們使用 Docker 來建立一個一致且易於管理的開發環境。然而,將應用程式部署到生產環境時,考量的因素會更多,例如:

  • 效能:需要確保系統在高併發下仍能穩定運行。
  • 安全性:需要防止未授權的訪問,並保護使用者資料。
  • 可維護性:需要方便的部署、升級和監控機制。

2. 使用 Docker 部署到生產環境

(1)為什麼選擇 Docker 部署?

  • 一致性:確保開發、測試和生產環境的一致性,減少「在我這裡可以運行」的問題。
  • 可移植性:容器化的應用程式可以在任何支援 Docker 的平台上運行。
  • 資源隔離:每個容器都有自己的資源限制,增強系統的穩定性。

(2)調整 Docker Compose 設定

在生產環境中,我們需要對 Docker Compose 進行一些調整:

  • 分離環境設定:將開發和生產的設定檔分開,例如 docker-compose.dev.yml 和 docker-compose.prod.yml。
  • 使用環境變數:在 docker-compose.yml 中使用環境變數,避免將敏感資訊(如資料庫密碼)硬編碼在檔案中。
  • 設定適當的服務:在生產環境中,可以考慮增加 Nginx 作為反向代理,處理靜態資源和 SSL 加密。

範例:docker-compose.prod.yml

version: '3'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.prod
    env_file:
      - .env.prod
    depends_on:
      - db
    networks:
      - app-network
  db:
    image: mysql:8.0
    volumes:
      - db_data:/var/lib/mysql
    env_file:
      - .env.prod
    networks:
      - app-network
  nginx:
    image: nginx:stable
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/ssl:/etc/nginx/ssl
    depends_on:
      - app
    networks:
      - app-network

networks:
  app-network:

volumes:
  db_data:

(3)安全性考量

  • 限制容器權限:避免以 root 權限運行容器,設定適當的使用者。
  • 更新與修補:定期更新 Docker 映像檔,確保使用最新的安全補丁。
  • 防火牆設定:只開放必要的埠號,例如 HTTP(80)和 HTTPS(443),關閉其他不需要的埠號。

3. 部署流程

(1)建立 CI/CD 流程

使用 CI/CD 工具(如 Jenkins、GitLab CI、GitHub Actions),自動化部署流程:

  • 自動測試:在每次代碼提交後,自動執行單元測試和整合測試。
  • 自動構建:成功通過測試後,自動構建 Docker 映像檔。
  • 自動部署:將映像檔推送到映像倉庫,並在伺服器上拉取更新,重啟容器。

(2)監控與日誌

  • 監控工具:使用 Prometheus、Grafana 等工具,監控系統的資源使用情況和性能指標。
  • 集中化日誌:使用 ELK(Elasticsearch、Logstash、Kibana)或其他日誌管理工具,收集和分析應用程式日誌。

二、後端的重構與安全性

1. API 存取權限

在之前的開發中,我們已經實作了基本的身份驗證功能。但在實際的應用中,還需要考慮更細緻的權限控制。

(1)使用角色與權限

  • 角色(Role):定義使用者的身份,例如管理員、一般使用者、訪客等。
  • 權限(Permission):定義可以執行的操作,例如查看、編輯、刪除等。

實作方法:

  • 建立角色與權限的資料表:使用多對多的關係,將角色與權限關聯起來。
  • 分配角色給使用者:在 users 表中新增 role_id,或使用多對多的關係。

範例:

// 檢查使用者是否有權限
if ($user->hasPermission('edit_transaction')) {
    // 執行編輯操作
} else {
    // 回傳權限不足的錯誤訊息
}

2. 日誌記錄

為了方便日後的維護和問題排查,我們需要對關鍵操作進行日誌記錄。

(1)記錄使用者操作

  • 登入/登出:記錄使用者的登入和登出時間、IP 位址等。
  • 敏感操作:如刪除資料、修改重要資訊等,需要詳細記錄操作內容。

(2)實作方法

  • 使用 Laravel 的日誌功能:在操作的地方添加日誌記錄。
  • 資料庫記錄:建立一個 logs 表,將操作記錄存入資料庫,便於查詢和分析。

範例:

// 在控制器中
use Illuminate\Support\Facades\Log;

public function destroy($id)
{
    $transaction = Transaction::findOrFail($id);
    $transaction->delete();

    // 這裡用Laravel 內建的Loggin方法當表示範例
    Log::info('User ' . auth()->id() . ' deleted transaction ' . $id);

    return response()->json(null, 204);
}

3. 程式碼重構方向

隨著功能的增加,程式碼可能變得複雜且難以維護。我們需要進行重構,提升程式碼的可讀性和可維護性。

(1)使用服務層(Service Layer)

將業務邏輯從控制器中抽離,放入專門的服務類別中。

範例:

// app/Services/TransactionService.php
namespace App\Services;

class TransactionService
{
    public function createTransaction($data)
    {
        // 處理業務邏輯
        return Transaction::create($data);
    }
}

// 在控制器中
use App\Services\TransactionService;

public function store(Request $request, TransactionService $transactionService)
{
    $validatedData = $request->validate([...]);
    $transaction = $transactionService->createTransaction($validatedData);
    return response()->json($transaction, 201);
}

(2)使用表單請求(Form Request)

將資料驗證移到表單請求類別中,保持控制器的簡潔。

範例:

// app/Http/Requests/StoreTransactionRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreTransactionRequest extends FormRequest
{
    public function rules()
    {
        return [
            'amount' => 'required|numeric',
            // 其他驗證規則
        ];
    }
}

// 在控制器中
public function store(StoreTransactionRequest $request, TransactionService $transactionService)
{
    $transaction = $transactionService->createTransaction($request->validated());
    return response()->json($transaction, 201);
}

三、前端介面的可複用性

1. 元件化設計

為了提高前端程式碼的可維護性和重用性,我們需要將重複的部分抽取為可複用的元件。

(1)建立通用的表單元件

例如,交易紀錄、新增/編輯銀行帳戶等表單有許多相似的部分,我們可以建立通用的表單元件。

範例:

<!-- components/TransactionForm.vue -->
<template>
  <form @submit.prevent="handleSubmit">
    <!-- 表單欄位 -->
  </form>
</template>

<script setup>
defineProps(['transaction'])
const emit = defineEmits(['submit'])

function handleSubmit() {
  // 驗證和提交資料
  emit('submit', formData)
}
</script>

(2)在頁面中使用元件

<!-- pages/transactions/create.vue -->
<template>
  <TransactionForm @submit="createTransaction" />
</template>

<script setup>
import TransactionForm from '~/components/TransactionForm.vue'
import { useRouter } from 'vue-router'

const router = useRouter()

function createTransaction(data) {
  // 呼叫 API 建立交易
  $fetch('/api/transactions', { method: 'POST', body: data })
    .then(() => router.push('/transactions'))
}
</script>

2. 統一資料取得方式

每個頁面都需要從後端取得資料,我們可以使用 composables 來統一管理資料的取得。

(1)建立資料取得的 composable

// composables/useTransactions.js
export function useTransactions() {
  const transactions = ref([])
  const error = ref(null)

  async function fetchTransactions() {
    try {
      transactions.value = await $fetch('/api/transactions')
    } catch (e) {
      error.value = e
    }
  }

  return { transactions, error, fetchTransactions }
}

(2)在頁面中使用

<script setup>
import { useTransactions } from '~/composables/useTransactions'

const { transactions, error, fetchTransactions } = useTransactions()

onMounted(() => {
  fetchTransactions()
})
</script>

3. 使用全域的狀態管理

為了在不同的元件和頁面之間共享狀態,我們可以使用 Pinia。

範例:使用 Pinia

// stores/transactionStore.js
import { defineStore } from 'pinia'

export const useTransactionStore = defineStore('transaction', {
  state: () => ({
    transactions: [],
  }),
  actions: {
    async fetchTransactions() {
      this.transactions = await $fetch('/api/transactions')
    },
  },
})

在頁面中使用

<script setup>
import { useTransactionStore } from '~/stores/transactionStore'

const transactionStore = useTransactionStore()

onMounted(() => {
  transactionStore.fetchTransactions()
})
</script>

最後

今天,我們從系統環境、後端重構和前端優化三個面向,探討了如何進一步提升我們的個人財務管理系統。

  • 系統環境:透過 Docker 部署到生產環境,確保系統的一致性和可擴展性,同時加強安全性的考量。
  • 後端重構:引入角色與權限管理、日誌記錄,並透過服務層和表單請求進行程式碼重構,提升系統的安全性和可維護性。
  • 前端優化:透過元件化設計、統一資料取得方式和狀態管理,提升前端程式碼的可複用性和易維護性。

在獨自開發的過程中,時間和精力往往有限,但我們仍然需要兼顧系統的品質和結構。
希望透過今天的分享,能夠幫助大家在開發中找到效率與品質的平衡點。

接下來的日子裡,我們可以繼續優化我們的系統,加入更多實用的功能,並進一步提升使用者體驗。

讓我們繼續努力,打造出一個更加完善的應用程式!
感謝你的閱讀,如果你有任何問題或建議,歡迎在下方留言討論。我們下次見!


上一篇
D26 - 第三階段驗收:功能總結與未來規劃
下一篇
D28 - 從獨自奮鬥到團隊合作:溝通的藝術與技術的融合
系列文
我獨自開發 - 30天進化之路,掌握 Laravel + Nuxt30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言