If you are looking for practical answer here is one of my thought.
Lets say that you wanted to get the Customer Account and then modify it accordingly to the input from the API.
So writing the data layer or query would look something like this :
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
}
Save the code above naming customer_account.go
.
Now lets say that you want to decouple the database query from your business logic or in this case your DAL with BLL. you can use interface for that. Creating an interface type that match witch your model query method above like this :
type CustomerAccountInterface interface {
GetCustomerAccount (id int) (CustomerAccount,error)
SaveCustomerAccount(ca CustomerAccount) error
}
save it as customer_account_interface.go
.
And now we would like to write a business logic which will be responsible for the modifying the data and we will call the CusomerAccountInterface
to the business Logic. Since we're creating an API so wen we used handler for this :
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)
})
}
From the above approach we have decoupled the handler which is the business logic with the data access or query database so that we can create a unit test for the business Logic in the handler something like this :
Creating CustomerAccountMock
for to mock the result query from data access :
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
}
Now we can write out test something like this :
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.
}
}
Above just for getting the idea how to I approach on separate the data layer and business logic layer. you can refer to my complete source code here. The code refers to another test case like updating driver data but it is the same approach.
But there are some cons on this approach though, for me it is like writing a thousands article when it comes to testing, you have to be patience!.
So coming to your question
Is it necessary to have DAL and BLL in Go Web App?
Yes, it does. Separating the data access with the business logic layer it's important so that we can unit test it.
In the above example the logic is pretty simple, but imagine if you have a complex logic to manipulate the data and you are not separate the DAL and BLL. It will hurt you in the future and other developer when it comes to changes the logic or query.
Feeling afraid to change and frustrated when something gone wrong is definitely you want to avoid to happen in your professional life.