Here will see how to pass a database to a service layer. Service layer will have interface. Look at our main function
func Run() error {
fmt.Println("Application start up")
database, err := db.NewDatabase()
if err != nil {
fmt.Println("Error creating database")
}
if err := database.MigrateDB(); err != nil {
fmt.Println("failed to migrate database")
}
cmtService := comment.NewService(database)
...................................................................................
...................................................................................
fmt.Println("successfully connected to database")
return nil
}
func main() {
fmt.Println("Go rest api")
if err := Run(); err != nil {
fmt.Println("returned error")
}
}
In the main function, we create a database object by calling the NewDatabase() function. This function returns a pointer to a Database struct which contains a pointer to a sqlx.DB object. This sqlx.DB object is the connection pool to the database.
Look at our Database struct
type Database struct {
//here DB is abstract of database and maintain a connection pool
Client *sqlx.DB
}
Then we pass this Database object as an argument to the NewService() function which returns a pointer to a Service struct. In this way, we inject the database object into the service layer, making it available for use by the service layer.
Look at our NewService() constructor
type Store interface {
//repository layer should implement this
GetComment(context.Context, string) (Comment, error)
PostComment(context.Context, Comment) (Comment, error)
DeleteComment(context.Context, string) error
UpdateComment(context.Context, string, Comment) (Comment, error)
}
type Service struct {
Store Store
}
func NewService(store Store) *Service {
return &Service{
Store: store,
}
}
When we call a function in the service layer, it can then use the Store interface to communicate with the database. The Store interface defines the methods that the repository layer should implement to interact with the database. The Service struct then uses these methods to perform various CRUD operations on the database.
So in summary, the database object is created in the main function and passed to the service layer through the NewService() function. The service layer then uses the Store interface to interact with the database through the repository layer.
Another summary
In our example, the Store interface is defined to be implemented by the Database struct. The Database struct has a field named db of type *sql.DB.
When we call NewService, we pass a Database object which implements the Store interface. Since Database has a db field of type *sql.DB, which satisfies the Store interface, we can pass the Database object to NewService.
So when NewService is called with a Database object, it can access the db field of the Database object using the Store interface. This is possible because the Database struct implements the Store interface, and the db field of the Database struct is of type *sql.DB, which is what the Service methods expect to receive.