閒談軟體設計:邁向 Aggregate

更新 發佈閱讀 10 分鐘
圖片來源:ChatGPT 生成

圖片來源:ChatGPT 生成

農曆年後開工至今差不多過了一個禮拜,還是來聊聊軟體設計吧!這篇來聊一下 Aggregate,但不是聊 Aggregate 有什麼好處,要怎麼設計,這些內容在很多書和網路的文章都有,不用我再贅述,而是聊怎麼誤打誤撞把原本不是 Aggregate 的設計變成類 Aggregate 的設計。

起源

故事的起源是一個單純的功能,想像一下,在系統中,主管能發送 Google 表單,然後員工能填寫表單,而且表單發送出去後無法修改,不同員工之間在填寫時也沒有相互關係,因此一開始的設計很單純,由一個 Entity 代表表單:Survey,另一個 Entity 代表填寫的內容:Submission,並有 service 提供 use cases:發送表單、填寫表單和編輯已填寫的表單。

到這邊為止,其實沒什麼複雜的設計,大部分的邏輯都在 Entity 與 Request 物件內部,例如:檢查是否超過截止日,或是填寫的內容是否符合表單設定的規範,service 只是從 repository 取出 survey,呼叫對應的函式,如果都沒有違反的情況就儲存 submission 物件。

一致性問題

初期運作良好,直到第一個需要處理一致性問題的需求誕生:表單內容錯了怎麼辦?即便 UI/UX 有再次確認等避免發送錯誤內容的設計,但有設計過系統的都知道,使用者通常都沒在看,等到發現錯了再修改。

如果 Survey 和 Submission 之間是彼此獨立,這其實也沒什麼,就直接修改 survey 物件即可,但偏偏 Survey 和 Submission 是關聯的。

舉個簡單的例子,例如,原先有個問題是「你想搭配什麼飲料?」,選項有「可樂、橘子汁和氣泡水」,已經有一位使用者填寫橘子汁。後來發現選項有錯,要改成「可樂、季節果汁和氣泡水」,修改問題容易,但已經填寫的內容怎麼辦?保留不變更?自動改成季節果汁?

這恐怕不是系統能決定的,最理想的方式就是整個調查作廢,讓使用者再填寫一次,而這次用的是比較資料庫導向的做法,是的,這裡並沒有使用比較符合 Domain Driven Design 的做法,而是在 SubmissionRepository 中新增一個特別的函式 remove(surveyCriterion),然後 SurveyService 修改 survey 後,把已經填寫的內容全部作廢。

任何設計都是一種 trade off,這作法的優點是施工簡單,且當下並沒有使用該設計會有致命問題的缺點,是一個可以考慮的作法。

更複雜的一致性

最近,在收集到更多需求後,對這功能增加了許多設定與規範,這也導致不止 Survey 和 Submission 關聯更深,連 submission 物件間也出現了關聯,一個員工填寫的內容,會影響到之後員工填寫的內容,為了確保能符合這些規則,有幾個選項:

  • 悲觀鎖 — 在處理 Submission 時,必須先取得鎖,所有人都排隊一個一個處理
  • 樂觀鎖 — 先處理,當儲存時發現狀態已經變動,撤回再來一次 (參閱 閒談軟體設計:樂觀鎖 )

在考慮併發的頻率 (多少員工同時送出表單) 後,想先用樂觀鎖,但要使用樂觀鎖面臨到的一個問題是:誰擁有樂觀鎖?讓 Submission 持有?顯然行不通,一個 submission 的樂觀鎖無法確保另一個 submission 的一致性。

讓 Survey 持有?確實是一個選項,但卻讓我有點猶豫,如果讓 Survey 持有樂觀鎖,則勢必要讓 survey 持有所有的 submission 物件,這讓已經夠複雜的 survey 物件增加複雜度。

於是想到新增一個物件完成以下幾件事:

  • 持有樂觀鎖 — 確保規則不滿足時,會撤回儲存的動作
  • 持有 survey 物件 — 用來檢查規則
  • 持有所有的 submission 物件 — 用來檢查規則
  • 檢查所有要滿足的規則 — 確保一致性

但怎麼命名?這邊很難找到更好的名詞,因為 Survey 本身就是最合適的,和 ChatGPT 反覆討論十幾個名詞後,最後決定用 SurveyCampaign 作為這個物件的名稱,一個由 Survey 發起的活動。

此時,原本放在 service 的邏輯都被移到這個新的 SurveyCampaign 物件裡,第 8 行確保填寫的內容都符合表單的規範,第 10 行確保填寫的內容彼此之間沒有違反規範,如果有違反,都會拋出對應的 Exception (這裡為了排版長度簡化成一個 exception,實際是好幾個不同的 domain exception)。

此外,新增 SurveyCampaignRepository,負責檢查樂觀鎖,讓 service 只需要取出 campaign,呼叫 campaign.submit(request),最後再存回,如果同時有另一個員工先送出了,樂觀鎖會阻擋此次的儲存。

未完待續

就這樣,新增的需求都滿足了。當時順便問了一下 ChatGPT,這樣的設計符合什麼 pattern?此時 ChatGPT 洋洋灑灑地說了幾個 pattern,但仔細檢驗會發現有問題,例如,我就找不到 Derived Aggregate 的出處,接著問出處,沒想到 ChatGPT 的回覆讓我傻眼:

「Derived Aggregate」不是一個正式、標準化、可被引用的經典 pattern 名稱。

可見任何 AI 的回答都是需要自己去考證的,一臉正經胡說八道可是 AI 的強項。

先不管 Derived Aggregate 是否存在,目前的設計離真正的 Aggregate 其實還有一段距離,在《Domain-Driven Design — Tackling Complexity in the Heart of Software》書中對於 Aggregate 有提到幾個規則:

  • 是有全域 ID 的 root entity 並負責檢查不變量
  • 內部的 entities 在 aggregate 範圍內有內部 ID 確保唯一性
  • Aggregate 邊界外部不能直接持有內部 entities 的參考
  • 只能從資料庫取得 Aggregate root,內部的 entities 都只能透過關聯取得
  • Aggregate 內部的物件可以持有其他 aggregate 物件
  • 刪除 Aggregate 時須確保邊界內的內部物件都被清除
  • 任何 Aggregate 內部物件的修改都須確保整個 Aggregate 的不變量被滿足

目前的設計是否同時滿足上述規則有很大的討論空間,特別是第三點和第四點,由於是透過重構的方式調整設計,在沒有修改原有的 service 的情況下,系統是允許繞過 SurveyCampaign 取得 Survey 和 Submission 物件。

要考量到效率時,不是所有 Survey 的查詢都會同時要取得 Submission,反之,有時會需要取得單一筆 Submission,而不需要 Survey。如果要符合上述規則,就必須將原有的查詢都改成 Read Model (參閱 閒談軟體設計:Read Model),不然有機會違反最後一條規則,這重構的範圍又太大了。

最後一條規則又更麻煩,Submission 有提供函式作為使用者編輯時修改內容之用,如果要滿足這規則,一種方是是將這個函式的可見度,從 public 降成 package 或 protected,只有同個層級的 Aggregate 可以操作。

但同個層級的不一定只有 Aggregate,要真的滿足,Aggregate 回傳 entities 時,要進行一次 clone 的動作或是回傳 immutable interface (參閱 閒談軟體設計:Immutable Interface)。

所以,目前的設計只能說是有 Aggregate 精神的不完整實作,在滿足系統需求下的特殊解,如果要變成真正的 Aggregate 還有不少地方要調整。

小節

其實一開始並沒有要用 Aggregate,單純是想要有一個樂觀鎖的載體,只是沒想到最後形成的設計,有那麼一點 Aggregate 的精神在裡面,從這例子可以說明:沒有什麼設計是一定要一次到位的,在符合需求的情況下慢慢重構優化都是有可能的,雖然重構有時候會比較花時間 (文章中只討論到物件設計上的修改,但其實背後還有 database schema 的搭配調整),與其擔心設計不到位,過度設計往往是更頭痛難以處理的。

留言
avatar-img
Spirit 異想世界
58會員
119內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
Spirit 異想世界的其他內容
2026/02/21
作者分享了 B2B 產品在迭代開發過程中所遇到的挑戰,特別是在追求功能完整性的過程中,如何避免設計變得過於複雜,進而影響使用者體驗。文中探討了從使用者回饋、市場變化到團隊內部認知的變化,並提出了在產品開發中「踩煞車」的重要性,以確保產品能真正符合使用者需求,而非徒增複雜度。
Thumbnail
2026/02/21
作者分享了 B2B 產品在迭代開發過程中所遇到的挑戰,特別是在追求功能完整性的過程中,如何避免設計變得過於複雜,進而影響使用者體驗。文中探討了從使用者回饋、市場變化到團隊內部認知的變化,並提出了在產品開發中「踩煞車」的重要性,以確保產品能真正符合使用者需求,而非徒增複雜度。
Thumbnail
2026/02/14
分享在不同公司期間,與客戶進行實地拜訪的豐富經,這些故事不僅是單純的客戶訪談紀錄,更揭示了產品開發過程中可能遇到的挑戰、客戶的真實痛點,以及從數據以外的角度全面理解市場需求的洞見。文章強調了工程師實地訪談的重要性,並鼓勵讀者若有機會應多加嘗試,從中獲取寶貴的經驗與啟發。
Thumbnail
2026/02/14
分享在不同公司期間,與客戶進行實地拜訪的豐富經,這些故事不僅是單純的客戶訪談紀錄,更揭示了產品開發過程中可能遇到的挑戰、客戶的真實痛點,以及從數據以外的角度全面理解市場需求的洞見。文章強調了工程師實地訪談的重要性,並鼓勵讀者若有機會應多加嘗試,從中獲取寶貴的經驗與啟發。
Thumbnail
2026/02/07
本文探討了在團隊成長過程中,如何建立關注數據的文化,並從開發數據、工程營運數據、產品營運數據及使用者體驗數據四個面向,說明數據在產品決策、優化與進步中的重要性。讓團隊相信數據的價值是建立數據文化的關鍵,並鼓勵工程師不僅關注程式碼,更要關心產品的整體成效。
Thumbnail
2026/02/07
本文探討了在團隊成長過程中,如何建立關注數據的文化,並從開發數據、工程營運數據、產品營運數據及使用者體驗數據四個面向,說明數據在產品決策、優化與進步中的重要性。讓團隊相信數據的價值是建立數據文化的關鍵,並鼓勵工程師不僅關注程式碼,更要關心產品的整體成效。
Thumbnail
看更多
你可能也想看
Thumbnail
賽勒布倫尼科夫以流亡處境回望蘇聯電影導演帕拉贊諾夫的舞台作品,以十段寓言式殘篇,重新拼貼記憶、暴力與美學,並將審查、政治犯、戰爭陰影與「形式即政治」的劇場傳統推到台前。本文聚焦於《傳奇:帕拉贊諾夫的十段殘篇》的舞台美術、音樂與多重扮演策略,嘗試解析極權底下不可言說之事,將如何成為可被觀看的公共發聲。
Thumbnail
賽勒布倫尼科夫以流亡處境回望蘇聯電影導演帕拉贊諾夫的舞台作品,以十段寓言式殘篇,重新拼貼記憶、暴力與美學,並將審查、政治犯、戰爭陰影與「形式即政治」的劇場傳統推到台前。本文聚焦於《傳奇:帕拉贊諾夫的十段殘篇》的舞台美術、音樂與多重扮演策略,嘗試解析極權底下不可言說之事,將如何成為可被觀看的公共發聲。
Thumbnail
柏林劇團在 2026 北藝嚴選,再次帶來由布萊希特改編的經典劇目《三便士歌劇》(The Threepenny Opera),導演巴里・柯斯基以舞台結構與舞台調度,重新向「疏離」進行提問。本文將從觀眾慾望作為戲劇內核,藉由沉浸與疏離的辯證,解析此作如何再次照見觀眾自身的位置。
Thumbnail
柏林劇團在 2026 北藝嚴選,再次帶來由布萊希特改編的經典劇目《三便士歌劇》(The Threepenny Opera),導演巴里・柯斯基以舞台結構與舞台調度,重新向「疏離」進行提問。本文將從觀眾慾望作為戲劇內核,藉由沉浸與疏離的辯證,解析此作如何再次照見觀眾自身的位置。
Thumbnail
本文深入解析臺灣劇團「晃晃跨幅町」對易卜生經典劇作《海妲.蓋柏樂》的詮釋,從劇本歷史、聲響與舞臺設計,到演員的主體創作方法,探討此版本如何讓經典劇作在當代劇場語境下煥發新生,滿足現代觀眾的觀看慾望。
Thumbnail
本文深入解析臺灣劇團「晃晃跨幅町」對易卜生經典劇作《海妲.蓋柏樂》的詮釋,從劇本歷史、聲響與舞臺設計,到演員的主體創作方法,探討此版本如何讓經典劇作在當代劇場語境下煥發新生,滿足現代觀眾的觀看慾望。
Thumbnail
《轉轉生》為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,融合舞蹈、音樂、時尚和視覺藝術,透過身體、服裝與群舞結構,回應殖民歷史、城市經驗與祖靈記憶的交錯。本文將從服裝設計、身體語彙與「輪迴」的「誕生—死亡—重生」結構出發,分析《轉轉生》如何以當代目光,形塑去殖民視角的奈及利亞歷史。
Thumbnail
《轉轉生》為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,融合舞蹈、音樂、時尚和視覺藝術,透過身體、服裝與群舞結構,回應殖民歷史、城市經驗與祖靈記憶的交錯。本文將從服裝設計、身體語彙與「輪迴」的「誕生—死亡—重生」結構出發,分析《轉轉生》如何以當代目光,形塑去殖民視角的奈及利亞歷史。
Thumbnail
設計變更的應對策略 當我們手中已有了不同的架構圖時,下一步該如何進行?
Thumbnail
設計變更的應對策略 當我們手中已有了不同的架構圖時,下一步該如何進行?
Thumbnail
想知道怎樣設計標誌,就要知道什麼是LOGO? LOGO是:英語到對商標擁有公司的識別和推廣的作用,透過形象的logo可以讓消費者記住公司主體和品牌文化。 LOGO特點和特性清單: 準確性:LOGO無論要說明什麼、指示什麼,無論是寓意或像徵,其意義必須準確。尤其是共工標識,首先要易懂
Thumbnail
想知道怎樣設計標誌,就要知道什麼是LOGO? LOGO是:英語到對商標擁有公司的識別和推廣的作用,透過形象的logo可以讓消費者記住公司主體和品牌文化。 LOGO特點和特性清單: 準確性:LOGO無論要說明什麼、指示什麼,無論是寓意或像徵,其意義必須準確。尤其是共工標識,首先要易懂
Thumbnail
產品開發的成功,除了品質,更在於是否能夠在適當的時程內推出並滿足客戶需求。 身為開發、設計人員,從文中提供的三個角度來思考,以確保產品與公司的競爭力。
Thumbnail
產品開發的成功,除了品質,更在於是否能夠在適當的時程內推出並滿足客戶需求。 身為開發、設計人員,從文中提供的三個角度來思考,以確保產品與公司的競爭力。
Thumbnail
在當今數位化的商業環境中,新商品開發已經不再僅依賴傳統的市場調查和直覺決策。隨著大數據和人工智慧技術的迅速發展,數據驅動的開發策略成為推動創新和保持市場競爭力的關鍵。本文將探討如何利用數據驅動的方法來優化新商品開發流程,從而更有效地滿足消費者需求,提高產品成功率。
Thumbnail
在當今數位化的商業環境中,新商品開發已經不再僅依賴傳統的市場調查和直覺決策。隨著大數據和人工智慧技術的迅速發展,數據驅動的開發策略成為推動創新和保持市場競爭力的關鍵。本文將探討如何利用數據驅動的方法來優化新商品開發流程,從而更有效地滿足消費者需求,提高產品成功率。
Thumbnail
網站系統規劃設計 形象網站 普遍公司使用的網站型態,能夠系統化分類、多個頁面切換。形象網站屬於品牌門面(網路名片),網站風格會直接帶給使用者個性化感受,無形中的網路數據也可開發潛在客戶。
Thumbnail
網站系統規劃設計 形象網站 普遍公司使用的網站型態,能夠系統化分類、多個頁面切換。形象網站屬於品牌門面(網路名片),網站風格會直接帶給使用者個性化感受,無形中的網路數據也可開發潛在客戶。
Thumbnail
軟體開發領域。越來越多的開發人員開始利用AI來進行陌生開發(Uncharted Development),這是指進行尚未經歷過的領域或技術的開發工作。在本文中,我將介紹利用AI從事陌生開發的三大好處。
Thumbnail
軟體開發領域。越來越多的開發人員開始利用AI來進行陌生開發(Uncharted Development),這是指進行尚未經歷過的領域或技術的開發工作。在本文中,我將介紹利用AI從事陌生開發的三大好處。
Thumbnail
這篇文章將會講述團隊開發中,關於設計與製作的平衡與注意的事情。
Thumbnail
這篇文章將會講述團隊開發中,關於設計與製作的平衡與注意的事情。
Thumbnail
無論您是跨境SOHO、小額批發商、傳統實體平臺賣家、營銷專家等,snailcn SAAS 都能滿足。 核心優勢 對標shopify,店鋪裝修,diy模板,非技術人員也可以更改模板內容。 SEO友好,預加載seo meta信息,多方面增強頁面收錄權重 全面支持CDN,加速您的網站js css加載 雲圖
Thumbnail
無論您是跨境SOHO、小額批發商、傳統實體平臺賣家、營銷專家等,snailcn SAAS 都能滿足。 核心優勢 對標shopify,店鋪裝修,diy模板,非技術人員也可以更改模板內容。 SEO友好,預加載seo meta信息,多方面增強頁面收錄權重 全面支持CDN,加速您的網站js css加載 雲圖
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News