最近看了劉潤的底層邏輯,書中有一個小節是流程、制度與系統,看完後突然有點想寫些什麼的衝動。

這些商業管理的抽象名詞,乍看之下可能跟寫程式沒什麼關係,畢竟我自己習慣的是討論程式碼本身,或是工具的效率... 等議題,但其實這三個概念好像可以跟軟體工程搭上邊?

▼ 這三個概念:流程、制度與系統,可以畫成一個類別圖

classDiagram class 流程{ - 由有序活動組成 - 定義工作執行順序 } class 制度{ - 約束和規範行為 - 提供行為準則 } class 系統{ - 由多個部件組成 - 實現特定業務功能 } 流程 -- 制度 : 制度可能規範流程 流程 -- 系統 : 系統可自動化流程 制度 -- 系統 : 系統需遵循制度

這三者之間的關係,為我們提供了一個強大的框架,可以用來檢視和建立複雜的前端應用。

流程 Process

流程就是一種順序,他定義了從起點到終點的一系列有序的活動,不管是使用者體驗(UX) 或是數據處理 (Data),流程都與之息息相關。一個清晰、順暢的流程,可以讓使用者的體驗 UP UP!

使用者互動流程

我們舉一個簡單卻又直觀的流程為例子:電商網站的忘記密碼流程,將每一個步驟拆分如下 ⬇️

  1. 使用者點擊「忘記密碼」按鈕
  2. 系統引導至「輸入註冊信箱」的頁面
  3. 使用者輸入信箱後,畫面(前端) 顯示「已寄出信件」等提示
  4. 使用者點擊信件中的連結,連到「設定新密碼」頁面
  5. 使用者輸入並確認新密碼後,系統跳出「密碼設定成功」的提示,並回到登入頁

這樣的流程設計,就符合前面的類別圖中提到的兩點:

  • 有序:每個順序都是有其意義的,無法插隊,也不能跳過某個步驟
  • 定義執行順序:我們將每個步驟拆開,一步步地寫出來,可以更好地釐清整個執行的順序

有了好的流程設計,我們就能搭配狀態機 (State Machine) 的概念,來管理這樣的流程。

▼ 以下用 React 框架為例,可以使用 useStateuseReducer 來追蹤當前的流程狀態,並根據狀態渲染對應的 UI

import React, { useReducer } from 'react';

// 定義所有狀態
const initialState = {
  email: '',
  status: 'idle',   // 'idle' | 'submitting' | 'success' | 'error'
  error: null
};

// 定義狀態機 reducer
function reducer(state, action) {
  switch (action.type) {
    case 'INPUT_CHANGE':
      return {
        ...state,
        email: action.payload
      };
    case 'SUBMIT':
      return {
        ...state,
        status: 'submitting',
        error: null
      };
    case 'SUCCESS':
      return {
        ...state,
        status: 'success'
      };
    case 'FAILURE':
      return {
        ...state,
        status: 'error',
        error: action.payload
      };
    default:
      return state;
  }
}

function PasswordResetForm() {
  const [state, dispatch] = useReducer(reducer, initialState);

  const handleSubmit = async (e) => {
    e.preventDefault();
    dispatch({ type: 'SUBMIT' });

    try {
      await api.sendResetEmail(state.email);
      dispatch({ type: 'SUCCESS' });
    } catch (err) {
      dispatch({ type: 'FAILURE', payload: err.message });
    }
  };

  if (state.status === 'success') {
    return <p>重設密碼的信件已寄至您的信箱,請查收。</p>;
  }

  return (
    <form onSubmit={handleSubmit}>
      {state.status === 'error' && <p className="error">{state.error}</p>}
      <input
        type="email"
        value={state.email}
        onChange={(e) => dispatch({ type: 'INPUT_CHANGE', payload: e.target.value })}
        placeholder="請輸入您的註冊信箱"
        disabled={state.status === 'submitting'}
      />
      <button type="submit" disabled={state.status === 'submitting'}>
        {state.status === 'submitting' ? '傳送中...' : '傳送重設信件'}
      </button>
    </form>
  );
}

數據處理流程

除了畫面外,前端也需要處理數據,通常會從後端取得原始資料,再加工並顯示在畫面上。這中間的過程就包含了數據的轉換、過濾、排序... 等處理。

同樣舉個例子,我要在儀表板頁面顯示本月銷售冠軍的商品列表,步驟流程拆解如下:

  1. Fetch Data:向後端 API 發送請求取得資料
  2. Transform Data:將取得的資料進行格式化,例如將 timestamp 轉成常見的日期格式
  3. Sort Data:根據銷售額進行排序
  4. Render Data:將處理好的數據資料顯示在畫面上

這邊推薦使用 Pinia 做流程管理工具,他的 actions 負責非同步操作與相關業務邏輯,還能直接修改 state,讓數據流的定義更直觀。

import { defineStore } from 'pinia';
import { api } from '@/services/api'; // 假設有一個封裝好的 api 服務

export const useProductStore = defineStore('products', {
  // 定義流程中需要管理的數據
  state: () => ({
    topProducts: [],
    isLoading: false,
    error: null,
  }),

  // 定義完整的數據處理流程
  actions: {
    async fetchTopProducts() {
      this.isLoading = true;
      this.error = null;
      try {
        // 步驟一:取得資料 (Fetch)
        const rawData = await api.fetchMonthlySales();

        // 步驟二 & 三:轉換 (Transform) 與排序 (Sort)
        const processedData = rawData
          .map(p => ({
            ...p,
            saleDate: new Date(p.saleDate).toLocaleDateString(), // 格式化日期
          }))
          .sort((a, b) => b.salesVolume - a.salesVolume); // 按銷量排序

        // 將最終結果存入 state
        this.topProducts = processedData;

      } catch (err) {
        console.error("Failed to fetch top products", err);
        this.error = '無法取得商品資料,請稍後再試。';
      } finally {
        this.isLoading = false;
      }
    },
  },
});

使用 pinia 的好處是可以避免把數據資料分散在多個元件中,這樣容易造成資料散落且不一致的問題,現在我們需要資料,就統一從 pinia 這個源頭取得,確保資料的統一與可靠性。

AD

制度 Rules

我覺得制度可以看成是提升團隊程式碼品質的一個規則,也可以是軟體應用中使用者的權限規定 ... 等,以下就用這兩塊功能為例與大家分享我的看法。

程式碼規範制度

在團隊協作中,常常會因為程式碼的風格而吵翻天 XD,例如最經典的幾個問題:

  1. 結尾要不要分號?
  2. 縮排用 space 還是 tab?幾格?
  3. 變數命名是駝峰式還是字首大寫?要不要底線?

這些看似不影響程式碼開發,卻能左右開發者心情的問題,如果不好好處理,很容易造成開發者的混亂與日後維護的噩夢,所以程式碼規範是一種制度,需要我們共同遵守。

最常見的 ESLint 這套工具,我們利用這套工具將這個制度自動化,強制執行語法規則。

使用者存取的權限制度

在每個會員系統中,不同角色的使用者會擁有不同的權限,這樣的權限體系,又何嘗不是一種制度呢?

以後台管理系統為例,我們通常會有這些角色,並賦予這些角色不同的權限:

  • 管理員:可以觀看所有頁面,也可以管理使用者
  • 編輯者:可以觀看跟內容相關的頁面,也可以編輯內容,但不能管理或刪除使用者
  • 訪客:只能看儀表板的公開資料

在做權限功能時,要擋住的第一道關卡就是頁面的存取權,這時我會使用路由守衛 (Route Guards) 來做權限檢查。

{
  path: '/client/user',
  label: '用戶列表',
  // 對應的頁面元件,我多包一層 ProtectedRoute 做檢查,表示此頁需權限保護
  element: (
    <ProtectedRoute>
      <ListClientUserPage />
    </ProtectedRoute>
  ),
  // 權限要求,使用者需擁有其中一個才能訪問
  permission: ['read user', 'manage user'],
}

系統 System

一個個的元件組合起來,就是我們熟知的系統,讓我們再一次複習類別圖,更能加深系統、流程與制度之間的關係:

  • 系統可以將流程自動化
  • 系統需要遵循制度
classDiagram class 流程{ - 由有序活動組成 - 定義工作執行順序 } class 制度{ - 約束和規範行為 - 提供行為準則 } class 系統{ - 由多個部件組成 - 實現特定業務功能 } 流程 -- 制度 : 制度可能規範流程 流程 -- 系統 : 系統可自動化流程 制度 -- 系統 : 系統需遵循制度

以前端開發而言,我們談論的系統小至一個元件,大致整個應用架構都是有可能的。因為一個獨立的元件,往往也有獨立的流程和獨立的制度。

前端元件化系統

現在我們的開發愈來愈趨向「元件化」,尤其使用了前端框架後,這樣的趨勢已不可避免。我們讓每個元件都有獨立的功能、狀態和邏輯,但他們又像樂高一樣要組合在一起,才能實現完整的功能。

我們會將畫面拆解成獨立、可重用的元件,透過 props 或 events 定義它們之間的接口,致力做出一個高內聚、低耦合的系統。而原子設計更是將系統化的概念推到極致,從原子、分子,到組織,一層一層的建構出 UI 系統。

前後端整合系統

我們再將視角拉遠一些,會發現前端其實只是整個業務系統的一部分,因為前端通常會和後端、資料庫、第三方服務 ... 等共同構成一個更大的系統。

例如,前後端會透過 API (像是 RESTful 或 GraphQL) 來交換資料,進行協作,這就是兩個子系統合作的方式。

以電商為例,假設在購物車系統中,前端的 AddToCart 元件透過呼叫後端的 API,再進一步與庫存管理、訂單處理等其他子系統互動,才能共同完成「將商品加入購物車」這個業務功能。

流程、制度與系統

前面我把這三個概念分開說明,但它們並非獨立作業,而是可以彼此補足、相互成就。最後我會用一個完整的例子,來跟大家分享我怎麼看待「流程、制度與系統」,以及如何讓它們緊密結合、發揮整體效益。

現在我們要做一個能讓使用者安全登入的功能:

  • 流程 - 登入的步驟
    1. 使用者輸入帳號密碼
    2. 點擊登入按鈕,前端畫面顯示 Loading 狀態
    3. 發送請求至後端進行驗證
    4. 如果驗證成功,就跳到主頁;如果驗證失敗,就顯示錯誤訊息
  • 制度 - 登入的規則
    • 安全的制度:密碼在傳輸過程中必須加密,如果嘗試登入超過 X 次,帳號會被臨時鎖定
    • 驗證的制度:帳號是合法的 Email 格式,密碼長度要大於 8 位且英數混合
    • 程式碼的制度:處理登入的邏輯函式,必須符合團隊的規範(如果有)
  • 系統 - 實現登入的架構與工具
    • 元件系統:<LoginForm /> 元件,封裝了輸入框、按鈕和錯誤提示等畫面與狀態
    • 狀態管理系統:集中管理使用者的登入狀態、Token 和個人資訊 .... 等
    • 路由系統:設定路由守衛,確保登入後才能看到頁面
    • API 系統:封裝跟後端 API 有關的處理

這個登入的系統中,會用到登入的「流程」,而系統的實作細節 (如表單) 需要遵守「制度」,制度又反過來影響「流程」的體驗。真的是息息相關,缺一不可。

小結

在邊寫邊思考的過程中,發現自己也學到了很多。

未來當我開始一個新專案或是新功能時,也許會嘗試用這三個視角來結構化我的任務:

  • 我要實現的流程是什麼?
  • 我要遵守哪些制度?
  • 我應該要用怎樣的系統來組合?

最後也希望這樣的分享能幫助到各位的日常開發,有任何問題都歡迎留言討論唷。