// SqlGdbc (SQL Go database connection) is a wrapper for SQL database handler ( can be *sql.DB or *sql.Tx)
// It should be able to work with all SQL data that follows SQL standard.
typeSqlGdbcinterface {
Exec(querystring, args...interface{}) (sql.Result, error)
Prepare(querystring) (*sql.Stmt, error)
Query(querystring, args...interface{}) (*sql.Rows, error)
QueryRow(querystring, args...interface{}) *sql.Row// If need transaction support, add this interface
Transactioner
}
// SqlDBTx is the concrete implementation of sqlGdbc by using *sql.DB
typeSqlDBTxstruct {
DB*sql.DB
}
// SqlConnTx is the concrete implementation of sqlGdbc by using *sql.Tx
typeSqlConnTxstruct {
DB*sql.Tx
}
// Transactioner is the transaction interface for database handler
// It should only be applicable to SQL database
typeTransactionerinterface {
// Rollback a transaction
Rollback() error// Commit a transaction
Commit() error// TxEnd commits a transaction if no errors, otherwise rollback
// txFunc is the operations wrapped in a transaction
TxEnd(txFuncfunc() error) error// TxBegin gets *sql.DB from receiver and return a SqlGdbc, which has a *sql.Tx
TxBegin() (SqlGdbc, error)
}
typeRegistrationUseCaseInterfaceinterface {
...// ModifyAndUnregister change user information and then unregister the user based on the User.Id passed in.
// It is created to illustrate transaction, no real use.
ModifyAndUnregister(user*model.User) error// ModifyAndUnregisterWithTx change user information and then unregister the user based on the User.Id passed in.
// It supports transaction
// It is created to illustrate transaction, no real use.
ModifyAndUnregisterWithTx(user*model.User) error// EnableTx enable transaction support on use case. Need to be included for each use case needs transaction
// It replaces the underline database handler to sql.Tx for each data service that used by this use case
EnableTxer
}
// EnableTxer is the transaction interface for use case layer
typeEnableTxerinterface {
EnableTx()
}
// The use case of ModifyAndUnregister without transaction
func (ruc*RegistrationUseCase) ModifyAndUnregister(user*model.User) error {
returnmodifyAndUnregister(ruc, user)
}
// The use case of ModifyAndUnregister with transaction
func (ruc*RegistrationUseCase) ModifyAndUnregisterWithTx(user*model.User) error {
tdi, err:=ruc.TxDataInterface.TxBegin()
iferr!=nil {
returnerrors.Wrap(err, "")
}
ruc.EnableTx()
returntdi.TxEnd(func() error {
// wrap the business function inside the TxEnd function
returnmodifyAndUnregister(ruc, user)
})
}
// The business function will be wrapped inside a transaction and inside a non-transaction function
// It needs to be written in a way that every error will be returned so it can be catched by TxEnd() function,
// which will handle commit and rollback
funcmodifyAndUnregister(ruc*RegistrationUseCase, user*model.User) error {
udi:=ruc.UserDataInterfaceerr:=modifyUser(udi, user)
iferr!=nil {
returnerrors.Wrap(err, "")
}
err = unregisterUser(udi, user.Name)
iferr!=nil {
returnerrors.Wrap(err, "")
}
returnnil
}
func (ruc*RegistrationUseCase) EnableTx() {
// Only UserDataInterface need transaction support here. If there are other data services need it,
// then they also need to enable transaction here
ruc.UserDataInterface.EnableTx(ruc.TxDataInterface)
}
// TxDataInterface represents operations needed for transaction support.
// It only needs to be implemented once for each database
// For sqlGdbc, it is implemented for SqlDBTx in transaction.go
typeTxDataInterfaceinterface {
// TxBegin starts a transaction. It gets a DB handler from the receiver and return a TxDataInterface, which has a
// *sql.Tx inside. Any data access wrapped inside a transaction will go through the *sql.Tx
TxBegin() (TxDataInterface, error)
// TxEnd is called at the end of a transaction and based on whether there is an error, it commits or rollback the
// transaction.
// txFunc is the business function wrapped in a transaction
TxEnd(txFuncfunc() error) error// Return the underline transaction handler, sql.Tx
GetTx() gdbc.SqlGdbc
}
// This interface needs to be included in every data service interface that needs transaction support
typeEnableTxerinterface {
// EnableTx enables transaction, basically it replaces the underling database handle sql.DB with sql.Tx
EnableTx(dataInterfaceTxDataInterface)
}
// UserDataInterface represents interface for user data access through database
typeUserDataInterfaceinterface {
...Update(user*model.User) (rowsAffectedint64, errerror)
// Insert adds a user to a database. The returned resultUser has a Id, which is auto generated by database
Insert(user*model.User) (resultUser*model.User, errerror)
// Need to add this for transaction support
EnableTxer
}
// TxDataSql is the generic implementation for transaction for SQL database
// You only need to do it once for each SQL database
typeTxDataSqlstruct {
DBgdbc.SqlGdbc
}
func (tds*TxDataSql) TxEnd(txFuncfunc() error) error {
returntds.DB.TxEnd(txFunc)
}
func (tds*TxDataSql) TxBegin() (dataservice.TxDataInterface, error) {
sqlTx, error:=tds.DB.TxBegin()
tdi:=TxDataSql{sqlTx}
tds.DB = tdi.DBreturn&tdi, error
}
func (tds*TxDataSql) GetTx() gdbc.SqlGdbc {
returntds.DB
}