From a71e09367c66cc6522e64f715e0ada484ecc45c5 Mon Sep 17 00:00:00 2001 From: Michael Thomson Date: Fri, 7 Feb 2025 21:15:34 -0500 Subject: [PATCH] refactor by domain --- cmd/main.go | 46 +++-------- internal/models/todo.go | 7 -- .../todo/inmemorytodorepository.go | 58 ------------- internal/repositories/todo/todorepository.go | 18 ----- internal/todo/repository/inmemory/inmemory.go | 60 ++++++++++++++ internal/todo/repository/repository.go | 26 ++++++ .../repository/sqlite/sqlite.go} | 26 +++--- internal/todo/service/service.go | 81 +++++++++++++++++++ internal/todo/service/service_test.go | 24 ++++++ 9 files changed, 216 insertions(+), 130 deletions(-) delete mode 100644 internal/models/todo.go delete mode 100644 internal/repositories/todo/inmemorytodorepository.go delete mode 100644 internal/repositories/todo/todorepository.go create mode 100644 internal/todo/repository/inmemory/inmemory.go create mode 100644 internal/todo/repository/repository.go rename internal/{repositories/todo/sqlitetodorepository.go => todo/repository/sqlite/sqlite.go} (64%) create mode 100644 internal/todo/service/service.go create mode 100644 internal/todo/service/service_test.go diff --git a/cmd/main.go b/cmd/main.go index cd4d088..fbd9193 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -6,11 +6,13 @@ import ( "log" "gitea.michaelthomson.dev/mthomson/habits/internal/migrate" - "gitea.michaelthomson.dev/mthomson/habits/internal/models" - repositories "gitea.michaelthomson.dev/mthomson/habits/internal/repositories/todo" + todosqliterepository "gitea.michaelthomson.dev/mthomson/habits/internal/todo/repository/sqlite" + todoservice "gitea.michaelthomson.dev/mthomson/habits/internal/todo/service" + _ "github.com/mattn/go-sqlite3" ) func main() { + // create db pool db, err := sql.Open("sqlite3", "./habits.db") if err != nil { log.Fatal("Failed to open db pool") @@ -21,41 +23,17 @@ func main() { // run migrations migrate.Migrate(db); - repo := repositories.NewSqliteTodoRepository(db) + // create repos + todoRepository := todosqliterepository.NewSqliteTodoRepository(db) + + // create services + todoService := todoservice.NewTodoService(&todoRepository) // create todo - newTodo := &models.Todo{Name: "clean dishes", Done: false} - - err = repo.Create(newTodo) - - if err != nil { - log.Fatal(err) - } - - fmt.Printf("id of created todo: %d\n", newTodo.Id) - - // update todo - newTodo.Done = true - - err = repo.Update(newTodo) - - if err != nil { - log.Fatal(err) - } - - fmt.Printf("todo is now done: %t\n", newTodo.Done) - - // delete todo - err = repo.Delete(newTodo.Id) - - if err != nil { - log.Fatal(err) - } - - // get todo - _, err = repo.GetById(newTodo.Id) - + todo := todoservice.NewTodo("clean dishes", false) + newTodo, err := todoService.CreateTodo(todo) if err != nil { log.Fatal(err) } + fmt.Printf("ID of created todo: %d", newTodo.Id) } diff --git a/internal/models/todo.go b/internal/models/todo.go deleted file mode 100644 index e7ff132..0000000 --- a/internal/models/todo.go +++ /dev/null @@ -1,7 +0,0 @@ -package models - -type Todo struct { - Id int64 - Name string - Done bool -} diff --git a/internal/repositories/todo/inmemorytodorepository.go b/internal/repositories/todo/inmemorytodorepository.go deleted file mode 100644 index 25bab4a..0000000 --- a/internal/repositories/todo/inmemorytodorepository.go +++ /dev/null @@ -1,58 +0,0 @@ -package repositories - -import "gitea.michaelthomson.dev/mthomson/habits/internal/models" - -type InMemoryTodoRepository struct { - db map[int64]*models.Todo - id int64 -} - -func NewInMemoryTodoRepository() InMemoryTodoRepository { - return InMemoryTodoRepository{ - db: make(map[int64]*models.Todo), - id: 1, - } -} - - -func (r *InMemoryTodoRepository) GetById(id int64) (*models.Todo, error) { - todo, found := r.db[id] - - if !found { - return todo, ErrNotFound - } - - return todo, nil -} - -func (r *InMemoryTodoRepository) Create(todo *models.Todo) error { - todo.Id = r.id - r.db[r.id] = todo - r.id++ - - return nil -} - -func (r *InMemoryTodoRepository) Update(todo *models.Todo) error { - _, found := r.db[todo.Id] - - if !found { - return ErrNotFound - } - - r.db[todo.Id] = todo - - return nil -} - -func (r *InMemoryTodoRepository) Delete(id int64) error { - _, found := r.db[id] - - if !found { - return ErrNotFound - } - - delete(r.db, id) - - return nil -} diff --git a/internal/repositories/todo/todorepository.go b/internal/repositories/todo/todorepository.go deleted file mode 100644 index d2c637e..0000000 --- a/internal/repositories/todo/todorepository.go +++ /dev/null @@ -1,18 +0,0 @@ -package repositories - -import ( - "errors" - - "gitea.michaelthomson.dev/mthomson/habits/internal/models" -) - -var ( - ErrNotFound error = errors.New("Todo cannot be found") -) - -type TodoRepository interface { - Create(todo *models.Todo) error - GetById(id int64) (*models.Todo, error) - Update(todo *models.Todo) error - Delete(id int64) error -} diff --git a/internal/todo/repository/inmemory/inmemory.go b/internal/todo/repository/inmemory/inmemory.go new file mode 100644 index 0000000..249e9ca --- /dev/null +++ b/internal/todo/repository/inmemory/inmemory.go @@ -0,0 +1,60 @@ +package inmemory + +import ( + "gitea.michaelthomson.dev/mthomson/habits/internal/todo/repository" +) + +type InMemoryTodoRepository struct { + db map[int64]repository.TodoRow + id int64 +} + +func NewInMemoryTodoRepository() InMemoryTodoRepository { + return InMemoryTodoRepository{ + db: make(map[int64]repository.TodoRow), + id: 1, + } +} + + +func (r *InMemoryTodoRepository) GetById(id int64) (repository.TodoRow, error) { + todo, found := r.db[id] + + if !found { + return todo, repository.ErrNotFound + } + + return todo, nil +} + +func (r *InMemoryTodoRepository) Create(todo repository.TodoRow) (repository.TodoRow, error) { + todo.Id = r.id + r.db[r.id] = todo + r.id++ + + return todo, nil +} + +func (r *InMemoryTodoRepository) Update(todo repository.TodoRow) error { + _, found := r.db[todo.Id] + + if !found { + return repository.ErrNotFound + } + + r.db[todo.Id] = todo + + return nil +} + +func (r *InMemoryTodoRepository) Delete(id int64) error { + _, found := r.db[id] + + if !found { + return repository.ErrNotFound + } + + delete(r.db, id) + + return nil +} diff --git a/internal/todo/repository/repository.go b/internal/todo/repository/repository.go new file mode 100644 index 0000000..13b9d7d --- /dev/null +++ b/internal/todo/repository/repository.go @@ -0,0 +1,26 @@ +package repository + +import ( + "errors" +) + +var ( + ErrNotFound error = errors.New("Todo cannot be found") +) + +type TodoRow struct { + Id int64 + Name string + Done bool +} + +func NewTodoDbModel(name string, done bool) *TodoRow { + return &TodoRow{Name: name, Done: done} +} + +type TodoRepository interface { + Create(todo TodoRow) (TodoRow, error) + GetById(id int64) (TodoRow, error) + Update(todo TodoRow) error + Delete(id int64) error +} diff --git a/internal/repositories/todo/sqlitetodorepository.go b/internal/todo/repository/sqlite/sqlite.go similarity index 64% rename from internal/repositories/todo/sqlitetodorepository.go rename to internal/todo/repository/sqlite/sqlite.go index d8fbff7..2d72d2c 100644 --- a/internal/repositories/todo/sqlitetodorepository.go +++ b/internal/todo/repository/sqlite/sqlite.go @@ -1,9 +1,9 @@ -package repositories +package sqlite import ( "database/sql" - "gitea.michaelthomson.dev/mthomson/habits/internal/models" + "gitea.michaelthomson.dev/mthomson/habits/internal/todo/repository" ) type SqliteTodoRepository struct { @@ -16,15 +16,15 @@ func NewSqliteTodoRepository(db *sql.DB) SqliteTodoRepository { } } -func (r *SqliteTodoRepository) GetById(id int64) (*models.Todo, error) { - todo := &models.Todo{} +func (r *SqliteTodoRepository) GetById(id int64) (repository.TodoRow, error) { + todo := repository.TodoRow{} row := r.db.QueryRow("SELECT * FROM todo WHERE id = ?;", id) - err := row.Scan(todo.Id, todo.Name, todo.Done) + err := row.Scan(&todo.Id, &todo.Name, &todo.Done) if err != nil { if err == sql.ErrNoRows { - return todo, ErrNotFound + return todo, repository.ErrNotFound } return todo, err @@ -33,25 +33,25 @@ func (r *SqliteTodoRepository) GetById(id int64) (*models.Todo, error) { return todo, nil } -func (r *SqliteTodoRepository) Create(todo *models.Todo) error { +func (r *SqliteTodoRepository) Create(todo repository.TodoRow) (repository.TodoRow, error) { result, err := r.db.Exec("INSERT INTO todo (name, done) VALUES (?, ?)", todo.Name, todo.Done) if err != nil { - return err + return repository.TodoRow{}, err } id, err := result.LastInsertId() if err != nil { - return err + return repository.TodoRow{}, err } todo.Id = id - return nil + return todo, nil } -func (r *SqliteTodoRepository) Update(todo *models.Todo) error { +func (r *SqliteTodoRepository) Update(todo repository.TodoRow) error { result, err := r.db.Exec("UPDATE todo SET name = ?, done = ? WHERE id = ?", todo.Name, todo.Done, todo.Id) if err != nil { @@ -65,7 +65,7 @@ func (r *SqliteTodoRepository) Update(todo *models.Todo) error { } if rowsAffected == 0 { - return ErrNotFound + return repository.ErrNotFound } return nil @@ -85,7 +85,7 @@ func (r *SqliteTodoRepository) Delete(id int64) error { } if rowsAffected == 0 { - return ErrNotFound + return repository.ErrNotFound } return nil diff --git a/internal/todo/service/service.go b/internal/todo/service/service.go new file mode 100644 index 0000000..b1514f4 --- /dev/null +++ b/internal/todo/service/service.go @@ -0,0 +1,81 @@ +package service + +import "gitea.michaelthomson.dev/mthomson/habits/internal/todo/repository" + +type Todo struct { + Id int64 + Name string + Done bool +} + +func NewTodo(name string, done bool) Todo { + return Todo{Name: name, Done: done} +} + +func TodoFromTodoRow(todoRow repository.TodoRow) Todo { + return Todo{Id: todoRow.Id, Name: todoRow.Name, Done: todoRow.Done} +} + +func TodoRowFromTodo(todo Todo) repository.TodoRow { + return repository.TodoRow{Id: todo.Id, Name: todo.Name, Done: todo.Done} +} + +type TodoGetter interface { + GetTodo(id int64) (Todo, error) +} + +type TodoCreater interface { + CreateTodo(todo Todo) (Todo, error) +} + +type TodoDeleter interface { + DeleteTodo(id int64) error +} + +type TodoUpdater interface { + UpdateTodo(todo Todo) error +} + +type TodoService struct { + repo repository.TodoRepository +} + +func NewTodoService(todoRepo repository.TodoRepository) *TodoService { + return &TodoService{todoRepo} +} + +func (s *TodoService) GetTodo(id int64) (Todo, error) { + todo, err := s.repo.GetById(id) + + if err != nil { + return Todo{}, err + } + + return TodoFromTodoRow(todo), err +} + +func (s *TodoService) CreateTodo(todo Todo) (Todo, error) { + todoRow := TodoRowFromTodo(todo) + + newTodoRow, err := s.repo.Create(todoRow) + + if err != nil { + return Todo{}, err + } + + return TodoFromTodoRow(newTodoRow), err +} + +func (s *TodoService) DeleteTodo(id int64) error { + err := s.repo.Delete(id); + + return err +} + +func (s *TodoService) UpdateTodo(todo Todo) error { + todoRow := TodoRowFromTodo(todo) + + err := s.repo.Update(todoRow) + + return err +} diff --git a/internal/todo/service/service_test.go b/internal/todo/service/service_test.go new file mode 100644 index 0000000..c7cecea --- /dev/null +++ b/internal/todo/service/service_test.go @@ -0,0 +1,24 @@ +package service_test + +import ( + "testing" + + "gitea.michaelthomson.dev/mthomson/habits/internal/todo/repository/inmemory" + "gitea.michaelthomson.dev/mthomson/habits/internal/todo/service" +) + +func TestCreateTodo(t *testing.T) { + todoRepository := inmemory.NewInMemoryTodoRepository() + + todoService := service.NewTodoService(&todoRepository) + + t.Run("Create todo", func(t *testing.T) { + todo := service.NewTodo("clean dishes", false) + + _, err := todoService.CreateTodo(todo) + + if err != nil { + t.Error(err) + } + }) +}