要不要把商業邏輯寫在預儲程序裡?

在開發企業級應用程式時,要不要把資料處理邏輯寫在 stored procedure 裡面,也是個常見的問題。最近剛好碰到一個狀況,就順便寫點筆記。

我記得在哪裡看過一個原則(也許是 Dino Esposito 的書):如果要寫 stored procedure,裡面應該只包含資料邏輯,而不要包含商業邏輯。

我認為這建議合理。不過其實我基本上不太寫 stored procedure,也不贊成這麼做--除了一些特殊原因,例如效能。一方面,大概是自己的 SQL 太弱;另一方面,有時候不太容易明確區分資料邏輯與商業邏輯,一不小心就「一個邏輯,各自表述」:同一件工作,團隊中有的人選擇寫在 stored procedure 中,有的人認為應該包在 business class 裡,結果就是商業邏輯散落各處,造成維護上的麻煩。

最近在修改一個既有系統的 bug 時,碰到一個狀況,跟 stored procedure 稍微扯上點關係,就順便記錄一下。

是這樣的:該系統的一個基本開發原則是程式碼裡面不可以寫 SQL 指令,所以任何對資料庫的操作都必須寫成相應的 stored procedure(底下簡稱 SP),供應用程式呼叫。也因為如此,大部分的(也許是全部的)商業邏輯都藏在各 SP 裡面。這種做法有個方便之處:將來要修改商業邏輯時,只要到資料庫裡面就能找到了。然而,實務上會不會變成資料庫裡面有一份商業邏輯,應用程式的商業邏輯層又重複寫一份呢?這也值得考慮。

既然是以 SP 來負責處理商業邏輯,寫程式時很自然就會發展出一個慣例:程式中必定有某個類別會提供一個與該 SP 名稱相同(或雷同)的方法,而且它們的參數也都大致一樣。比如說,假設某 SP 的名稱叫做 sp_CheckSomeRule,在商業邏輯層中的某個服務類別就會有一個對應的方法:CheckSomeRule()。只要寫法一致,問題不大。

然後,在追蹤一個 bug 時,我發現問題根源在 sp_CheckSomeRule 裡面。為了修正該問題,必須為那個 SP 增加一個參數。於是我修改了 SP,然後從用戶端程式碼一路追蹤至伺服器端,找出所有與該 SP 對應的方法,並為它們加上新的參數。

然後我發現我一共修改了下列程式檔案(依後端至前端順序列出):
  • MyApp.AppService.Domain.DataAccess.IFooDataAccess 介面
  • MyApp.AppService.Domain.DataAccess.FooDataAccess (上述介面的實作類別)
  • MyApp.AppService.Domain.Contracts.IFooContract 介面
  • MyApp.AppService.Domain.Services.IFooService 介面
  • MyApp.AppService.BusinessLogic.FooService 類別
  • MyApp.AppService.Services.FooContractService 類別
  • MyApp.Aplpication.Proxy.FooProxy 類別
  • MyApp.Application.BusinessLogic.FooManager 類別

這些介面和類別裡面都有 CheckSomeRule() 方法,而且那些類別幾乎是一個接著一個串連呼叫其他類別。比如說,當使用者點擊某按鈕,程式某處會呼叫 FooManager.CheckSomeRule(),然後該方法又去呼叫 FooProxy 的 CheckSomeRule(),然後是 FooContractService 或 FooService,最後到 FooDataAccess。
[碎碎念]  我還看到一個 stored procedure,用途是新增一筆記錄。假設該程序的名稱叫做 sp_AddCategory,它需要兩個參數:name、originalName。第二個參數看起來怪怪的....看了原始碼,才知道如果第二個參數不為空字串,就會將整個資料表中所有分類名稱符合 originalName 參數的資料全替換成 name 參數值。私下猜測,這大概是為了避免寫太多 stored procedure 而臨時起意的省事作法。傷腦筋的是,如果要重構此 stored procedure,將它一分為二,這又得改好多東西...Orz

有點瘋狂?

此系統的架構,實體部署的 tier 就有四、五層,所以在設計時將應用程式拆成多個 layers 也不令人意外。目前我還不明白是否一定要這麼多層,但這個部份的設計的確值得思量。

此外,雖然程式寫法一致,修改時毫無困難,但就是覺得....怪怪的。

解決方法?

如果基本架構已經無法改變,層層串連呼叫無可避免,使用 Introduce Parameter Object 技巧把方法所需的參數重構一下,應該就可以省下一些工夫。但這也只是解決一部分的問題,在真實世界裡總是沒有這麼單純....

Post Comments

技術提供:Blogger.