I am going to create a project in Go, but I want to separate my tiers. My goal is create a package that has code something like the DAO pattern, i.e. I wish that my caller should only communicate with the interface of the service (which might be another project) and not with the implementation. This is to avoid the situation of a future change to the database, the caller should not change and should be transparent to this change.
I was thinking of creating a project db that contains a file called persistence with a package persistence as well
http://play.golang.org/p/O9b93F4LJp
package persistence
// Interface that have the CRUD which I need to use from service(another project)
type Recorder interface {
Selectkey([]byte) (err error)
Insert([]byte, []byte) (err error)
Update([]byte, []byte) (err error)
Delete([]byte) (err error)
}
// Struct for Oracle
type ManageDataOracle struct {
}
// Struct for binaryTree
type ManageDataBInaryTree struct {
}
// Struct for MongoDB
type ManageDataMongoDb struct {
}
// Implemtations for Oracle
func (memory *ManageDataOracle) SelectKey(pKey []byte) error {
// Logic for Oracle Here
return nil
}
func (memory *ManageDataOracle) Insert(pKey []byte, pValue []byte) error {
// Logic for Oracle Here
return nil
}
func (memory *ManageDataOracle) Update(pKey []byte, pValue []byte) error {
// Logic for Oracle Here
return nil
}
func (memory *ManageDataOracle) Delete(pKey []byte) error {
// Logic for Oracle Here
return nil
}
// Implemtations for Binary Tree
func (memory *ManageDataBInaryTree) SelectKey(pKey []byte) error {
// Logic for Binary tree Here
return nil
}
func (memory *ManageDataBInaryTree) Insert(pKey []byte, pValue []byte) error {
// Logic for Binary tree Here
return nil
}
func (memory *ManageDataBInaryTree) Update(pKey []byte, pValue []byte) error {
// Logic for Binary tree Here
return nil
}
func (memory *ManageDataBInaryTree) Delete(pKey []byte) error {
// Logic for Binary tree Here
return nil
}
// Implemtations for Mongo DB
func (memory *ManageDataMongoDb) SelectKey(pKey []byte) error {
// Logic for MongoDB Here
return nil
}
func (memory *ManageDataMongoDb) Insert(pKey []byte, pValue []byte) error {
// Logic for MongoDB Here
return nil
}
func (memory *ManageDataMongoDb) Update(pKey []byte, pValue []byte) error {
// Logic for MongoDB Here
return nil
}
func (memory *ManageDataMongoDb) Delete(pKey []byte) error {
// Logic for MongoDB Here
return nil
}
Any advice on how to do this separation of concepts, or how the previous code should be called from another project?
You are on the right track but different persistence mechanisms will use different key types (for example an integer in an oracle and a string-like identifier in mongo).
The serialization of your data into []byte
should be handled by the abstraction layer because different underlying persistence engines will use different representations of your data. Consider for instance an integer and it's endianness.
Hence I would change the signatures of your interface to the following:
type Recorder interface {
SelectByKey(interface{}) error
DeleteByKey(interface{}) error
Insert(interface{}, interface{}) error
Update(interface{}, interface{}) error
}
this way you can also panic
inside, say SelectKey
if the given key is not of the correct type for a specific implementation. This wouldn't be possible if you serialized your key beforehand and would lead to trouble.
I think you need something like this:
https://github.com/qiangxue/golang-restful-starter-kit
This starter kit is designed to get you up and running with a project structure optimal for developing RESTful services in Go. The kit promotes the best practices that follow the SOLID principles and encourage writing clear and idiomatic Go code.
The kit provides the following features right out of the box
RESTful endpoints in the widely accepted format Standard CRUD operations of a database table JWT-based authentication Application configuration via environment variable and configuration file Structured logging with contextual information Panic handling and proper error response generation Automatic DB transaction handling Data validation Full test coverage