In many Go programming books, the author usually put data access logic within the same function that handles business logic. While I understand that this may be merely for teaching purposes, but I wonder if people actually separate BLL from DAL in real world development.
I have tried to apply layered design to my Go project but have not felt any benefits from it. For instance, my DAL functions are typically like this (in appdal package):
func GetCustomerAccountInfo (accountID int) (*sql.Rows, error) {
sql := `SELECT * FROM CUSTOMER_ACCOUNT WHERE ID = $1`
return GLOBAL_PSQL.Query(sql, accountID)
}
And my typical BLL functions would be something like this:
func NewCustomerAccountBLL (accountID int) (* CustomerAccountBLL) {
rows, err := appdal.GetCustomerAccountInfo(accountID)
// create an instance of CustomerAccountBLL (bll) and scan rows....
return &bll
}
Often I found that my BLL is essentially coupled with the database schema since scanning requires that I know which column that my query has read, so I found it's not a bad idea to merge some DAL function into BLL (such as merge the query to BLL instead). In addition, having a DAL also increases the amount of code that I have to maintain as well.
However, many software architects also encourages layered designs, and it makes sense to have BLL and DAL and assign clear responsibilities on each layer.
While I also understand design and patterns are not necessarily dependent on programming languages, but I often found little benefit from having both BLL and DAL in my project. Am I missing something important in design or Go? Thanks!
As noted by you, this question is not Go-specific and can apply to any language.
Here are some points I think you should consider regarding this:
As with other design matters, there is no right way of doing this, but the general practice is to actually separate Business Logic from Data Access.
Business Logic should not be tied to the actual Data Access implementation so that if you then decide to move away from SQL and save objects in plain files, or in a No-SQL storage you don't necessarily need to change the Business Logic layer.
In your case, the GetCustomerAccountInfo
returns sql.Rows
. This actually couples your Business Logic to that particular implementation. The usual practice is to return actual model objects (a CustomerAccount
for example).
Also note that your example is quite simple so it's true that even by separating it you might not see a lot of benefit. But sometimes things are not that simple.
Data Access logic might involve more complex queries joining tables, or even making separate queries inside a database transaction. By separating this you don't pollute Business Logic with these low level details. Also, you can change the underlying table structure by only making changes on the Data Access layer, without altering business logic layer.
Business Logic might also in term consist on more complex calculations such as merging different objects, applying defaults and perform domain validations. Separating this logic (which is independent of the storage being used) allows you to change Business Logic without having to necessarily change Data Access logic.
Basically, by separating this, you can develop (and also importantly: test) each of the Business and Data Access logic separately and have a more modular design.
I hope that helps.