如果您正在寻找实用的答案,这是我的想法之一。
假设您想要获取客户帐户,然后根据 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。该代码引用了另一个测试用例,例如更新驱动程序数据,但方法相同。
但是这种方法也有一些缺点,对我来说,就测试而言,这就像写数千篇文章一样,您必须耐心等待!。
所以来回答你的问题
Go Web App 中必须要有 DAL 和 BLL 吗?
是的,确实如此。将数据访问与业务逻辑层分开很重要,这样我们就可以对其进行单元测试。
在上面的示例中,逻辑非常简单,但是想象一下,如果您有一个复杂的逻辑来操作数据,并且您没有将 DAL 和 BLL 分开。当涉及到更改逻辑或查询时,它将在未来伤害您和其他开发人员。
在出现问题时感到害怕改变和沮丧绝对是你想要避免在你的职业生涯中发生的事情。