Go Web应用程序中是否需要DAL和BLL?

Go Web应用程序中是否需要DAL和BLL?,go,data-access-layer,business-logic-layer,Go,Data Access Layer,Business Logic Layer,在许多Go编程书籍中,作者通常将数据访问逻辑放在处理业务逻辑的同一个函数中。虽然我理解这可能只是为了教学目的,但我想知道,在现实世界的开发中,人们是否真的将BLL与DAL分开 我曾尝试将分层设计应用到我的Go项目中,但没有感觉到任何好处。例如,我的DAL函数通常如下(在appdal包中): 我典型的BLL函数是这样的: func NewCustomerAccountBLL (accountID int) (* CustomerAccountBLL) { rows, err := appd

在许多Go编程书籍中,作者通常将数据访问逻辑放在处理业务逻辑的同一个函数中。虽然我理解这可能只是为了教学目的,但我想知道,在现实世界的开发中,人们是否真的将BLL与DAL分开

我曾尝试将分层设计应用到我的Go项目中,但没有感觉到任何好处。例如,我的DAL函数通常如下(在appdal包中):

我典型的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并没有什么好处。我是否在设计或围棋中遗漏了一些重要的东西?谢谢

正如您所指出的,这个问题并不具体,可以适用于任何语言

以下是我认为你应该考虑的一些要点:

  • 与其他设计事项一样,没有正确的方法可以做到这一点,但一般做法是将业务逻辑与数据访问实际分离

  • 业务逻辑不应该绑定到实际的数据访问实现,因此,如果您决定离开SQL并将对象保存在普通文件中,或者保存在无SQL存储中,则不必更改业务逻辑层

  • 在您的例子中,
    GetCustomerAccountInfo
    返回
    sql.Rows
    。这实际上将您的业务逻辑与该特定实现相耦合。通常的做法是返回实际的模型对象(例如
    CustomerAccount

  • 还要注意的是,您的示例非常简单,因此即使将其分离,您也不会看到很多好处。但有时事情并不是那么简单

    • 数据访问逻辑可能涉及连接表的更复杂的查询,甚至在数据库事务中进行单独的查询。通过分离这些,您不会用这些低级细节污染业务逻辑。另外,您可以通过只在数据访问层上进行更改而不更改业务逻辑层来更改基础表结构

    • 业务逻辑也可能包括更复杂的计算,例如合并不同的对象、应用默认值和执行域验证。分离此逻辑(独立于所使用的存储)允许您更改业务逻辑,而无需更改数据访问逻辑

基本上,通过将其分离,您可以分别开发(也很重要:测试)每个业务和数据访问逻辑,并具有更模块化的设计


我希望这能有所帮助。

正如您所指出的,这个问题并不具体,可以适用于任何语言

以下是我认为你应该考虑的一些要点:

  • 与其他设计事项一样,没有正确的方法可以做到这一点,但一般做法是将业务逻辑与数据访问实际分离

  • 业务逻辑不应该绑定到实际的数据访问实现,因此,如果您决定离开SQL并将对象保存在普通文件中,或者保存在无SQL存储中,则不必更改业务逻辑层

  • 在您的例子中,
    GetCustomerAccountInfo
    返回
    sql.Rows
    。这实际上将您的业务逻辑与该特定实现相耦合。通常的做法是返回实际的模型对象(例如
    CustomerAccount

  • 还要注意的是,您的示例非常简单,因此即使将其分离,您也不会看到很多好处。但有时事情并不是那么简单

    • 数据访问逻辑可能涉及连接表的更复杂的查询,甚至在数据库事务中进行单独的查询。通过分离这些,您不会用这些低级细节污染业务逻辑。另外,您可以通过只在数据访问层上进行更改而不更改业务逻辑层来更改基础表结构

    • 业务逻辑也可能包括更复杂的计算,例如合并不同的对象、应用默认值和执行域验证。分离此逻辑(独立于所使用的存储)允许您更改业务逻辑,而无需更改数据访问逻辑

基本上,通过将其分离,您可以分别开发(也很重要:测试)每个业务和数据访问逻辑,并具有更模块化的设计


我希望这能有所帮助。

如果您正在寻找切实可行的答案,这里是我的想法之一

假设您想要获取客户帐户,然后根据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
}
type CustomerAccountInterface interface {
   GetCustomerAccount (id int) (CustomerAccount,error)
   SaveCustomerAccount(ca CustomerAccount) error
}
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.

  }

}
将代码保存在命名
customer\u帐户上方。转到

现在让我们假设您希望将数据库查询与业务逻辑分离,或者在本例中,将DAL与BLL分离。您可以使用这个接口。创建与上述模型查询方法匹配的接口类型,如下所示:

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
}
type CustomerAccountInterface interface {
   GetCustomerAccount (id int) (CustomerAccount,error)
   SaveCustomerAccount(ca CustomerAccount) error
}
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.

  }

}
将其另存为
customer\u account\u界面。转到

现在我们想编写一个业务逻辑,负责修改数据