Skip to content

Instantly share code, notes, and snippets.

@nomkhonwaan
Last active December 27, 2019 07:26
Show Gist options
  • Save nomkhonwaan/c19e8ccf373bce1feb9ff9249b38c67d to your computer and use it in GitHub Desktop.
Save nomkhonwaan/c19e8ccf373bce1feb9ff9249b38c67d to your computer and use it in GitHub Desktop.
repository-pattern-ใน-go-5dfc2ab8ec12f08f3649c983
package main
import (
"context"
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"strings"
"time"
)
// Post contains content of the blog platform
type Post struct {
ID int `bson:"id"`
Title string `bson:"title"`
Body string `bson:"body"`
CreatedAt *time.Time `bson:"createdAt"`
UpdatedAt *time.Time `bson:"updatedAt"`
}
// PostRepository is a contract for consuming data by business logic layer
type PostRepository interface {
// Return list of posts
FindAll() ([]Post, error)
// Return a single post by its ID
FindByID(id int) (Post, error)
}
// MariaDBPostRepository implements PostRepository for MariaDB
type MariaDBPostRepository struct {
*sql.DB
}
// FindAll returns list of posts
func (repo MariaDBPostRepository) FindAll() ([]Post, error) {
rows, err := repo.DB.Query(`SELECT id, title, body, created_at, updated_at FROM posts`)
if err != nil {
return nil, err
}
var posts []Post
for rows.Next() {
var p Post
err = rows.Scan(&p.ID, &p.Title, &p.Body, &p.CreatedAt, &p.UpdatedAt)
if err != nil {
return nil, err
}
posts = append(posts, p)
}
return posts, nil
}
// FindByID returns a single post by its ID
func (repo MariaDBPostRepository) FindByID(id int) (Post, error) {
row := repo.DB.QueryRow(`SELECT id, title, body, created_at, updated_at FROM posts`)
var p Post
err := row.Scan(&p.ID, &p.Title, &p.Body, &p.CreatedAt, &p.UpdatedAt)
return p, err
}
// MongoDBPostRepository implements PostRepository for MongoDB
type MongoDBPostRepository struct {
*mongo.Collection
}
// FindAll returns list of posts
func (repo MongoDBPostRepository) FindAll() ([]Post, error) {
cursor, err := repo.Collection.Find(context.Background(), bson.M{})
if err != nil {
return nil, err
}
var posts []Post
for cursor.Next(context.Background()) {
var p Post
err = cursor.Decode(&p)
if err != nil {
return nil, err
}
posts = append(posts, p)
}
return posts, nil
}
// FindByID returns a single post by its ID
func (repo MongoDBPostRepository) FindByID(id int) (Post, error) {
result := repo.Collection.FindOne(context.Background(), bson.M{"id": id})
var p Post
err := result.Decode(&p)
return p, err
}
func main() {
repo := newMariaDBRepository("<mariadb-user>", "<mariadb-password>", "<mariadb-host>", "<mariadb-database-name>")
// repo := newMongoDBRepository("mongodb://<mongodb-host>", "<mongodb-database-name>")
err := printAll(repo)
if err != nil {
panic(err)
}
}
func newMariaDBRepository(user, password, host, databaseName string) MariaDBPostRepository {
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:3306)/%s?parseTime=true", user, password, host, databaseName))
if err != nil {
panic(err)
}
return MariaDBPostRepository{db}
}
func newMongoDBRepository(uri, databaseName string) MongoDBPostRepository {
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(uri))
if err != nil {
panic(err)
}
db := client.Database(databaseName)
return MongoDBPostRepository{db.Collection("posts")}
}
func printAll(repo PostRepository) error {
posts, err := repo.FindAll()
if err != nil {
return err
}
for _, p := range posts {
fmt.Printf("A %s was created at %s had %d words\n", p.Title, p.CreatedAt, count(p.Body))
}
return nil
}
func count(s string) int {
return len(strings.Split(s, " "))
}
package main
import "testing"
type mockPostRepository struct {
findAllCalls int
}
func (repo *mockPostRepository) FindAll() ([]Post, error) {
repo.findAllCalls++
return []Post{
{
ID: 99,
Title: "test",
Body: "test",
},
}, nil
}
func (repo *mockPostRepository) FindByID(id int) (Post, error) {
return Post{
ID: 99,
Title: "test",
Body: "test",
}, nil
}
func TestPrintAll(t *testing.T) {
// Given
repo := &mockPostRepository{}
// When
printAll(repo)
// Then
if repo.findAllCalls != 1 {
t.Errorf("expect `findAll()` calls count 1 but got %d", repo.findAllCalls)
}
}
// MariaDBPostRepository implements PostRepository for MariaDB
type MariaDBPostRepository struct {
*sql.DB
}
// FindAll returns list of posts
func (repo MariaDBPostRepository) FindAll() ([]Post, error) {
rows, err := repo.DB.Query(`SELECT id, title, body, created_at, updated_at FROM posts`)
if err != nil {
return nil, err
}
var posts []Post
for rows.Next() {
var p Post
err = rows.Scan(&p.ID, &p.Title, &p.Body, &p.CreatedAt, &p.UpdatedAt)
if err != nil {
return nil, err
}
posts = append(posts, p)
}
return posts, nil
}
// FindByID returns a single post by its ID
func (repo MariaDBPostRepository) FindByID(id int) (Post, error) {
row := repo.DB.QueryRow(`SELECT id, title, body, created_at, updated_at FROM posts`)
var p Post
err := row.Scan(&p.ID, &p.Title, &p.Body, &p.CreatedAt, &p.UpdatedAt)
return p, err
}
// MongoDBPostRepository implements PostRepository for MongoDB
type MongoDBPostRepository struct {
*mongo.Collection
}
// FindAll returns list of posts
func (repo MongoDBPostRepository) FindAll() ([]Post, error) {
cursor, err := repo.Collection.Find(context.Background(), bson.M{})
if err != nil {
return nil, err
}
var posts []Post
for cursor.Next(context.Background()) {
var p Post
err = cursor.Decode(&p)
if err != nil {
return nil, err
}
posts = append(posts, p)
}
return posts, nil
}
// FindByID returns a single post by its ID
func (repo MongoDBPostRepository) FindByID(id int) (Post, error) {
result := repo.Collection.FindOne(context.Background(), bson.M{"id": id})
var p Post
err := result.Decode(&p)
return p, err
}
// Post contains content of the blog platform
type Post struct {
ID int `bson:"id"`
Title string `bson:"title"`
Body string `bson:"body"`
CreatedAt *time.Time `bson:"createdAt"`
UpdatedAt *time.Time `bson:"updatedAt"`
}
db.posts.drop();
db.posts.insert({ id: 1, title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', body: 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto', createdAt: new Date() });
db.posts.insert({ id: 2, title: 'qui est esse', body: 'est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla', createdAt: new Date() });
db.posts.insert({ id: 3, title: 'ea molestias quasi exercitationem repellat qui ipsa sit aut', body: 'et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut', createdAt: new Date() });
db.posts.insert({ id: 4, title: 'eum et est occaecati', body: 'ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit', createdAt: new Date() });
db.posts.insert({ id: 5, title: 'nesciunt quas odio', body: 'repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque', createdAt: new Date() });
DROP TABLE IF EXISTS posts;
CREATE TABLE posts (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
body TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME ON UPDATE CURRENT_TIMESTAMP
);
INSERT INTO posts (id, title, body) VALUES (1, 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto');
INSERT INTO posts (id, title, body) VALUES (2, 'qui est esse', 'est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla');
INSERT INTO posts (id, title, body) VALUES (3, 'ea molestias quasi exercitationem repellat qui ipsa sit aut', 'et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut');
INSERT INTO posts (id, title, body) VALUES (4, 'eum et est occaecati', 'ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit');
INSERT INTO posts (id, title, body) VALUES (5, 'nesciunt quas odio', 'repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque');
import "fmt"
import "strings"
// printAll will print all posts with its title, creation date-time and number of word in the body
func printAll(repo PostRepository) error {
posts, err := repo.FindAll()
if err != nil {
return err
}
for _, p := range posts {
fmt.Printf("A %s was created at %s had %d words\n", p.Title, p.CreatedAt, count(p.Body))
}
return nil
}
func count(s string) int {
return len(strings.Split(s, " "))
}
// PostRepository is a contract for consuming data by business logic layer
type PostRepository interface {
// Return list of posts
FindAll() ([]Post, error)
// Return a single post by its ID
FindByID(id int) (Post, error)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment