2017-05-19 126 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。当涉及到更改逻辑或查询时,它将在未来和其他开发人员中受到伤害。

感觉害怕改变和沮丧,当出现问题时肯定是你想避免发生在你的职业生涯中。

相关问题