2017-05-19 86 views
0

在許多Go編程書籍中,作者通常將數據訪問邏輯放在處理業務邏輯的相同函數中。雖然我明白這可能僅僅是爲了教學的目的,但我想知道人們是否真的將BLL從DAL中分離出來進行真實世界的開發。在Go Web App中是否需要DAL和BLL?

我試圖將分層設計應用到我的Go項目,但沒有感受到它的任何好處。例如,我的DAL功能通常是這樣的(在appdal包):

func GetCustomerAccountInfo (accountID int) (*sql.Rows, error) { 
    sql := `SELECT * FROM CUSTOMER_ACCOUNT WHERE ID = $1` 
    return GLOBAL_PSQL.Query(sql, accountID) 
} 

我的典型BLL功能將是這樣的:

func NewCustomerAccountBLL (accountID int) (* CustomerAccountBLL) { 
    rows, err := appdal.GetCustomerAccountInfo(accountID) 
    // create an instance of CustomerAccountBLL (bll) and scan rows.... 
    return &bll 
} 

我常常發現,我的BLL基本上再加與數據庫模式,因爲掃描要求我知道我的查詢讀取了哪一列,所以我發現將某些DAL函數合併到BLL中(例如將查詢合併到BLL)並不是一個壞主意。另外,擁有DAL也會增加我必須維護的代碼量。但是,許多軟件架構師也鼓勵分層設計,並且在每層上分配BLL和DAL並分配明確責任是有意義的。

雖然我也明白設計和模式不一定依賴於編程語言,但是我經常發現在我的項目中同時使用BLL和DAL幾乎沒有什麼好處。我在設計中缺少重要的東西嗎?謝謝!

+0

看看是否有幫助:http://stackoverflow.com/q/42791536/5779732,http://stackoverflow.com/a/42500771/5779732,http://stackoverflow.com/a/41824700/5779732 –

回答

3

正如您所指出的那樣,這個問題不適用於Go,可以應用於任何語言。

這裏有幾點我想你應該考慮對此:

  • 與其他設計問題,還有就是做這個的沒有正確的方式,但一般的做法是真正獨立的商業邏輯來自數據訪問。

  • 業務邏輯不應與實際的數據訪問實現綁定,因此,如果您決定離開SQL並將對象保存在普通文件或No-SQL存儲中,則不一定需要更改業務邏輯層。

  • 在你的情況下,GetCustomerAccountInfo返回sql.Rows。這實際上是將您的業務邏輯與特定實施相結合。通常的做法是返回實際的模型對象(例如CustomerAccount)。

  • 另請注意,您的示例非常簡單,因此即使將它分開也可能看不到太多好處。但有時事情並不那麼簡單。

    • 數據訪問邏輯可能涉及更復雜的查詢連接表,甚至在數據庫事務中進行單獨的查詢。通過分離,您不會污染具有這些低級別詳細信息的業務邏輯。此外,您可以通過僅更改數據訪問層來更改基礎表結構,而不更改業務邏輯層。

    • 業務邏輯也可能包含更復雜的計算,如合併不同對象,應用默認值和執行域驗證。分離這個邏輯(獨立於正在使用的存儲)使您可以更改業務邏輯,而無需更改數據訪問邏輯。

基本上,通過分離這一點,你可以開發(也是重要的:試驗)的業務和數據訪問邏輯分開,並有更多的模塊化設計。

我希望有幫助。

+0

非常感激。但是,根據分層設計,DAL不應該依賴於BLL的模型。讓DAL函數返回BLL模型也會引入依賴循環問題(BLL調用的DAL函數返回一個BLL對象)。這會成爲一個問題嗎?或者應該在這種情況下實施DAO? –

+0

您可以讓DAL返回一個與BLL對象分開的DAO。我只會這樣做,如果BLL對象足夠複雜,從DAL構建它們會將DAL層綁定到BLL邏輯。否則,如果BLL對象很簡單,那麼從DAO到BLL的映射最終只是一個微不足道的步驟,在這種情況下,從DAL返回BLL對象應該不成問題。這很大程度上取決於您的實際使用情況。 – eugenioy

1

如果你正在尋找實際的答案,這裏是我的想法之一。

假設您想獲取客戶帳戶,然後根據API的輸入對其進行修改。

所以寫入數據層或查詢會是這個樣子:

type CustomerAccount struct{ 
    id string // this data type will differ depends on your database. 
    Name string 
    Address string 
    Age int 
    // and any other attribute. this is just for example. 
} 


func (ca *CustomerAccount)GetCustomerAccount (id int) (CustomerAccount,error) { 
    var ca CostumerAccount 
    // write your query using any databases. 
    // return an error if error happens when you do query to the database. 

    return ca,nil 
} 

func (ca *CustomerAccount)SaveCustomerAccount(ca CustomerAccount) error { 
    // find and update the data from given CustomerAccount 
    return nil 
} 

保存上面的代碼命名customer_account.go

現在讓我們說你想從業務邏輯中分離數據庫查詢,或者在這種情況下,你的DAL和BLL分離。你可以使用該接口。創建匹配巫婆上述這樣的模型查詢方法,它的接口類型:

type CustomerAccountInterface interface { 
    GetCustomerAccount (id int) (CustomerAccount,error) 
    SaveCustomerAccount(ca CustomerAccount) error 
} 

保存爲customer_account_interface.go

現在我們想寫一個業務邏輯,它將負責修改數據,我們將把CusomerAccountInterface稱爲業務邏輯。因爲我們正在創建一個API,因此我們聞用於此處理程序:

func EditCustomerAccount(ca CustomerAccountInterface) http.Handler { 

    return http.HandleFunc(func(w http.ResponseWritter, r *http.Request){ 
    // get all the input from user using *http.Request like id and other input. 

    // get our CustomerAccount Data to modify it 
    customerAccount,err := ca.GetAccountCustomer(id) 

    // modify customerAccount Accordingly from the input data, for example 
     customerAccount.Name = inputName // you can change what ever you want with the data here. In this case we change the name only for example purpose. 

    // save your customerAccount to your database 
    err := ca.SaveCustomerAccount(customerAccount) 

    // send the response 200 ok resonse if no error happens 
    w.WriteHeader(http.StatusOk) 
    resp := response{} // you can create your response struct in other places. 
    resp.Message = "success update data" 
    json.NewEncoder(w).Encode(resp) 

    }) 

} 

從我們已經分離處理程序的上述方法是與數據訪問或查詢數據庫,以便我們可以創建一個單元中的業務邏輯測試在處理像這樣的商業邏輯:

創建CustomerAccountMock的嘲笑從數據訪問結果查詢:

type CustomerAccountMock struct { 
    err error 
    Data CutstomerAccount 
} 

func (ca *CustomerAccountMock)GetCustomerAccount (id int) (CustomerAccount,error) { 
    return ca.Data,nil 
} 

func (ca *CustomerAccountMock)SaveCustomerAccount(ca CustomerAccount) error { 
    return ca.err 
} 

現在我們可以寫出測試是這樣的:

func TestEditCustomerAccount(t *testing.T){ 
    testObjects := []struct{ 
    CMock CutomerAccountMock 
    }{ 
    { 
     CMock : CustomerAccountMock{ 
     err : errors.New("Test error") 
     Data : CustomerAccount{} // return an empty data 
     }, 
    }, 
    } 

    for _, testObject := range testObjects { 
    actualResponse := createRequestToHandler(testObject.CMock) 
    // here you can check your response from calling your request testing to your handler. 

    } 

} 

以上只是爲了讓我的想法如何在單獨的數據層和業務邏輯層上實現。你可以參考我的完整source code here。該代碼引用另一個測試用例,如更新驅動程序數據,但它是相同的方法。

但是,這種方法有一些缺點,對我來說,就像在測試時寫上千篇文章一樣,你必須要有耐心!

所以來你的問題

是否有必要在DAL和BLL中轉到Web應用程序?

是的,它的確如此。將數據訪問與業務邏輯層分離非常重要,以便我們可以對其進行單元測試。

在上面的例子中,邏輯非常簡單,但想象一下如果你有一個複雜的邏輯來操作數據,而且你沒有單獨的DAL和BLL。當涉及到更改邏輯或查詢時,它將在未來和其他開發人員中受到傷害。

感覺害怕改變和沮喪,當出現問題時肯定是你想避免發生在你的職業生涯中。

相關問題