Di contoh ini, UserService langsung terikat sama DataSource tertentu. Kalo kita mau ganti dari PostgreSQL ke MySQL misalnya, kita harus rombak banyak hal di UserService.
package main
import "fmt"
// DataSource contohnya "koneksi" ke DB tertentu
type DataSource struct {
// misal ada config dsb.
}
// Contoh method yg "nempel" ke DataSource
func (ds *DataSource) Connect() {
fmt.Println("Connected to a specific DB (e.g., PostgreSQL)")
}
type UserService struct {
ds DataSource
}
func (us *UserService) GetUser(username string) {
// Kalo kita ubah detail DataSource, disini juga bisa ikut berubah
us.ds.Connect()
fmt.Printf("Fetching user '%s' from a specific DB\n", username)
}
func main() {
userService := UserService{
ds: DataSource{},
}
userService.GetUser("JohnDoe")
}
Karena UserService bergantung langsung ke implementasi DataSource itu sendiri. Ganti engine DB bakal bikin UserService turut dirombak.
Nah, kalo yang ini UserService cuma tau โkontrakโ data store-nya lewat interface. Dalemannya mau PostgreSQL, MySQL, atau bahkan NoSQL juga bisa, asal implementasinya sesuai interface. Hasilnya, maintenance dan scaling jadi lebih enak.
package main
import "fmt"
// Interface yang cuma tau "kontrak"-nya aja
type DataStore interface {
Connect() error
FetchUser(username string) (string, error)
}
// Implementasi DataStore untuk PostgreSQL
type PostgresStore struct{}
func (ps *PostgresStore) Connect() error {
fmt.Println("Connected to PostgreSQL")
return nil
}
func (ps *PostgresStore) FetchUser(username string) (string, error) {
// misal di sini ada query ke PostgreSQL
return fmt.Sprintf("Data user '%s' dari PostgreSQL", username), nil
}
// Kalo mau implementasi MySQL, bikin struct & method mirip2 PostgresStore
type UserService struct {
ds DataStore
}
func NewUserService(store DataStore) *UserService {
return &UserService{
ds: store,
}
}
func (us *UserService) GetUser(username string) {
err := us.ds.Connect()
if err != nil {
fmt.Println("Error connecting:", err)
return
}
data, err := us.ds.FetchUser(username)
if err != nil {
fmt.Println("Error fetching user:", err)
return
}
fmt.Println(data)
}
func main() {
// Mau PostgresStore atau MySQLStore, silakan
store := &PostgresStore{}
userService := NewUserService(store)
userService.GetUser("JaneDoe")
}
Karena UserService cuma tau โkontrakโ DataStore. Dalemannya si PostgresStore bisa berubah, atau kita ganti ke MySQLStore, tapi UserService tetep jalan tanpa perlu diotak-atik.
Intinya, kalo kita pengen bikin sistem yang gampang di-maintenance dan di-scale, ๐๐จ๐จ๐ฌ๐ ๐๐จ๐ฎ๐ฉ๐ฅ๐ข๐ง๐ jauh lebih enak. Tapi pasti ada trade-off juga, kayak nambah overhead desain, interface, dsb.