閒談軟體設計:邁向 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會員
122內容數
這是從 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
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
在當今數位化的商業環境中,新商品開發已經不再僅依賴傳統的市場調查和直覺決策。隨著大數據和人工智慧技術的迅速發展,數據驅動的開發策略成為推動創新和保持市場競爭力的關鍵。本文將探討如何利用數據驅動的方法來優化新商品開發流程,從而更有效地滿足消費者需求,提高產品成功率。
Thumbnail
在當今數位化的商業環境中,新商品開發已經不再僅依賴傳統的市場調查和直覺決策。隨著大數據和人工智慧技術的迅速發展,數據驅動的開發策略成為推動創新和保持市場競爭力的關鍵。本文將探討如何利用數據驅動的方法來優化新商品開發流程,從而更有效地滿足消費者需求,提高產品成功率。
Thumbnail
這篇文章將會講述團隊開發中,關於設計與製作的平衡與注意的事情。
Thumbnail
這篇文章將會講述團隊開發中,關於設計與製作的平衡與注意的事情。
Thumbnail
網站系統規劃設計 形象網站 普遍公司使用的網站型態,能夠系統化分類、多個頁面切換。形象網站屬於品牌門面(網路名片),網站風格會直接帶給使用者個性化感受,無形中的網路數據也可開發潛在客戶。
Thumbnail
網站系統規劃設計 形象網站 普遍公司使用的網站型態,能夠系統化分類、多個頁面切換。形象網站屬於品牌門面(網路名片),網站風格會直接帶給使用者個性化感受,無形中的網路數據也可開發潛在客戶。
Thumbnail
想知道怎樣設計標誌,就要知道什麼是LOGO? LOGO是:英語到對商標擁有公司的識別和推廣的作用,透過形象的logo可以讓消費者記住公司主體和品牌文化。 LOGO特點和特性清單: 準確性:LOGO無論要說明什麼、指示什麼,無論是寓意或像徵,其意義必須準確。尤其是共工標識,首先要易懂
Thumbnail
想知道怎樣設計標誌,就要知道什麼是LOGO? LOGO是:英語到對商標擁有公司的識別和推廣的作用,透過形象的logo可以讓消費者記住公司主體和品牌文化。 LOGO特點和特性清單: 準確性:LOGO無論要說明什麼、指示什麼,無論是寓意或像徵,其意義必須準確。尤其是共工標識,首先要易懂
Thumbnail
軟體開發領域。越來越多的開發人員開始利用AI來進行陌生開發(Uncharted Development),這是指進行尚未經歷過的領域或技術的開發工作。在本文中,我將介紹利用AI從事陌生開發的三大好處。
Thumbnail
軟體開發領域。越來越多的開發人員開始利用AI來進行陌生開發(Uncharted Development),這是指進行尚未經歷過的領域或技術的開發工作。在本文中,我將介紹利用AI從事陌生開發的三大好處。
Thumbnail
設計變更的應對策略 當我們手中已有了不同的架構圖時,下一步該如何進行?
Thumbnail
設計變更的應對策略 當我們手中已有了不同的架構圖時,下一步該如何進行?
Thumbnail
產品開發的成功,除了品質,更在於是否能夠在適當的時程內推出並滿足客戶需求。 身為開發、設計人員,從文中提供的三個角度來思考,以確保產品與公司的競爭力。
Thumbnail
產品開發的成功,除了品質,更在於是否能夠在適當的時程內推出並滿足客戶需求。 身為開發、設計人員,從文中提供的三個角度來思考,以確保產品與公司的競爭力。
Thumbnail
《轉轉生》(Re:INCARNATION)為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,結合拉各斯街頭節奏、Afrobeat/Afrobeats、以及約魯巴宇宙觀的非線性時間,建構出關於輪迴的「誕生—死亡—重生」儀式結構。本文將從約魯巴哲學概念出發,解析其去殖民的身體政治。
Thumbnail
《轉轉生》(Re:INCARNATION)為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,結合拉各斯街頭節奏、Afrobeat/Afrobeats、以及約魯巴宇宙觀的非線性時間,建構出關於輪迴的「誕生—死亡—重生」儀式結構。本文將從約魯巴哲學概念出發,解析其去殖民的身體政治。
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
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