From 2c7f87e73afff9baf9d2eac7c679aff223201a65 Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Tue, 29 Sep 2020 21:51:18 -0300 Subject: [PATCH 01/12] feat: improvements in structures --- .idea/codeStyles/codeStyleConfig.xml | 5 + Makefile | 4 +- api/handler/book.go | 38 +- api/handler/book_test.go | 44 +-- api/handler/loan.go | 27 +- api/handler/loan_test.go | 38 +- api/handler/user.go | 34 +- api/handler/user_test.go | 39 +- api/main.go | 18 +- cmd/main.go | 11 +- domain/entity/{book/entity.go => book.go} | 6 +- domain/entity/book/interface.go | 28 -- domain/entity/book/manager.go | 55 --- domain/entity/book/mock/book.go | 368 ------------------ .../{book/fixture.go => book_fixture.go} | 6 +- domain/entity/entity.go | 2 - domain/entity/{user/entity.go => user.go} | 8 +- domain/entity/user/interface.go | 28 -- domain/entity/user/mock/user.go | 368 ------------------ .../{user/fixture.go => user_fixture.go} | 6 +- domain/repository/book/interface.go | 25 ++ .../book/repository_inmem.go | 34 +- .../book/repository_mysql.go | 39 +- domain/repository/user/interface.go | 25 ++ .../user/repository_inmem.go | 23 +- .../user/repository_mysql.go | 25 +- domain/usecase/book/interface.go | 15 + domain/usecase/book/mock/book.go | 123 ++++++ domain/usecase/book/service.go | 82 ++++ .../book/service_test.go} | 62 +-- domain/usecase/loan/interface.go | 7 +- domain/usecase/loan/mock/loan.go | 10 +- .../usecase/loan/{usecase.go => service.go} | 43 +- domain/usecase/loan/service_test.go | 99 +++++ domain/usecase/loan/usecase_test.go | 101 ----- domain/usecase/user/interface.go | 15 + domain/usecase/user/mock/user.go | 123 ++++++ .../manager.go => usecase/user/service.go} | 31 +- .../user/service_test.go} | 66 ++-- go.mod | 3 +- go.sum | 2 + 41 files changed, 821 insertions(+), 1265 deletions(-) create mode 100644 .idea/codeStyles/codeStyleConfig.xml rename domain/entity/{book/entity.go => book.go} (62%) delete mode 100644 domain/entity/book/interface.go delete mode 100644 domain/entity/book/manager.go delete mode 100644 domain/entity/book/mock/book.go rename domain/entity/{book/fixture.go => book_fixture.go} (63%) rename domain/entity/{user/entity.go => user.go} (58%) delete mode 100644 domain/entity/user/interface.go delete mode 100644 domain/entity/user/mock/user.go rename domain/entity/{user/fixture.go => user_fixture.go} (65%) create mode 100644 domain/repository/book/interface.go rename domain/{entity => repository}/book/repository_inmem.go (54%) rename domain/{entity => repository}/book/repository_mysql.go (76%) create mode 100644 domain/repository/user/interface.go rename domain/{entity => repository}/user/repository_inmem.go (65%) rename domain/{entity => repository}/user/repository_mysql.go (85%) create mode 100644 domain/usecase/book/interface.go create mode 100644 domain/usecase/book/mock/book.go create mode 100644 domain/usecase/book/service.go rename domain/{entity/book/manager_test.go => usecase/book/service_test.go} (50%) rename domain/usecase/loan/{usecase.go => service.go} (57%) create mode 100644 domain/usecase/loan/service_test.go delete mode 100644 domain/usecase/loan/usecase_test.go create mode 100644 domain/usecase/user/interface.go create mode 100644 domain/usecase/user/mock/user.go rename domain/{entity/user/manager.go => usecase/user/service.go} (54%) rename domain/{entity/user/manager_test.go => usecase/user/service_test.go} (55%) diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/Makefile b/Makefile index 68c5479..025583f 100644 --- a/Makefile +++ b/Makefile @@ -32,8 +32,8 @@ ci: dependencies test build-mocks: @go get github.com/golang/mock/gomock @go install github.com/golang/mock/mockgen - @~/go/bin/mockgen -source=domain/entity/book/interface.go -destination=domain/entity/book/mock/book.go -package=mock - @~/go/bin/mockgen -source=domain/entity/user/interface.go -destination=domain/entity/user/mock/user.go -package=mock + @~/go/bin/mockgen -source=domain/usecase/book/interface.go -destination=domain/usecase/book/mock/book.go -package=mock + @~/go/bin/mockgen -source=domain/usecase/user/interface.go -destination=domain/usecase/user/mock/user.go -package=mock @~/go/bin/mockgen -source=domain/usecase/loan/interface.go -destination=domain/usecase/loan/mock/loan.go -package=mock test: diff --git a/api/handler/book.go b/api/handler/book.go index a3947ad..7cad6f5 100644 --- a/api/handler/book.go +++ b/api/handler/book.go @@ -6,28 +6,28 @@ import ( "net/http" "time" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" + "github.com/eminetto/clean-architecture-go-v2/domain" "github.com/eminetto/clean-architecture-go-v2/api/presenter" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" - "github.com/codegangsta/negroni" + "github.com/eminetto/clean-architecture-go-v2/domain/entity" "github.com/gorilla/mux" ) -func listBooks(manager book.Manager) http.Handler { +func listBooks(service book.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error reading books" - var data []*book.Book + var data []*entity.Book var err error title := r.URL.Query().Get("title") switch { case title == "": - data, err = manager.List() + data, err = service.ListBooks() default: - data, err = manager.Search(title) + data, err = service.SearchBooks(title) } w.Header().Set("Content-Type", "application/json") if err != nil && err != domain.ErrNotFound { @@ -58,7 +58,7 @@ func listBooks(manager book.Manager) http.Handler { }) } -func createBook(manager book.Manager) http.Handler { +func createBook(service book.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error adding book" var input struct { @@ -74,7 +74,7 @@ func createBook(manager book.Manager) http.Handler { w.Write([]byte(errorMessage)) return } - b := &book.Book{ + b := &entity.Book{ ID: entity.NewID(), Title: input.Title, Author: input.Author, @@ -82,7 +82,7 @@ func createBook(manager book.Manager) http.Handler { Quantity: input.Quantity, CreatedAt: time.Now(), } - b.ID, err = manager.Create(b) + b.ID, err = service.CreateBook(b) if err != nil { log.Println(err.Error()) w.WriteHeader(http.StatusInternalServerError) @@ -107,7 +107,7 @@ func createBook(manager book.Manager) http.Handler { }) } -func getBook(manager book.Manager) http.Handler { +func getBook(service book.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error reading book" vars := mux.Vars(r) @@ -117,7 +117,7 @@ func getBook(manager book.Manager) http.Handler { w.Write([]byte(errorMessage)) return } - data, err := manager.Get(id) + data, err := service.GetBook(id) if err != nil && err != domain.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) @@ -143,7 +143,7 @@ func getBook(manager book.Manager) http.Handler { }) } -func deleteBook(manager book.Manager) http.Handler { +func deleteBook(service book.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error removing bookmark" vars := mux.Vars(r) @@ -153,7 +153,7 @@ func deleteBook(manager book.Manager) http.Handler { w.Write([]byte(errorMessage)) return } - err = manager.Delete(id) + err = service.DeleteBook(id) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) @@ -163,20 +163,20 @@ func deleteBook(manager book.Manager) http.Handler { } //MakeBookHandlers make url handlers -func MakeBookHandlers(r *mux.Router, n negroni.Negroni, manager book.Manager) { +func MakeBookHandlers(r *mux.Router, n negroni.Negroni, service book.UseCase) { r.Handle("/v1/book", n.With( - negroni.Wrap(listBooks(manager)), + negroni.Wrap(listBooks(service)), )).Methods("GET", "OPTIONS").Name("listBooks") r.Handle("/v1/book", n.With( - negroni.Wrap(createBook(manager)), + negroni.Wrap(createBook(service)), )).Methods("POST", "OPTIONS").Name("createBook") r.Handle("/v1/book/{id}", n.With( - negroni.Wrap(getBook(manager)), + negroni.Wrap(getBook(service)), )).Methods("GET", "OPTIONS").Name("getBook") r.Handle("/v1/book/{id}", n.With( - negroni.Wrap(deleteBook(manager)), + negroni.Wrap(deleteBook(service)), )).Methods("DELETE", "OPTIONS").Name("deleteBook") } diff --git a/api/handler/book_test.go b/api/handler/book_test.go index 9794b4d..7c51eab 100644 --- a/api/handler/book_test.go +++ b/api/handler/book_test.go @@ -11,10 +11,8 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain" "github.com/eminetto/clean-architecture-go-v2/domain/entity" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" - "github.com/codegangsta/negroni" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/book/mock" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book/mock" "github.com/golang/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" @@ -23,17 +21,17 @@ import ( func Test_listBooks(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockManager(controller) + manager := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() MakeBookHandlers(r, *n, manager) path, err := r.GetRoute("listBooks").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book", path) - b := book.NewFixtureBook() + b := entity.NewFixtureBook() manager.EXPECT(). - List(). - Return([]*book.Book{b}, nil) + ListBooks(). + Return([]*entity.Book{b}, nil) ts := httptest.NewServer(listBooks(manager)) defer ts.Close() res, err := http.Get(ts.URL) @@ -44,11 +42,11 @@ func Test_listBooks(t *testing.T) { func Test_listBooks_NotFound(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockManager(controller) + manager := mock.NewMockUseCase(controller) ts := httptest.NewServer(listBooks(manager)) defer ts.Close() manager.EXPECT(). - Search("book of books"). + SearchBooks("book of books"). Return(nil, domain.ErrNotFound) res, err := http.Get(ts.URL + "?title=book+of+books") assert.Nil(t, err) @@ -58,11 +56,11 @@ func Test_listBooks_NotFound(t *testing.T) { func Test_listBooks_Search(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockManager(controller) - b := book.NewFixtureBook() + manager := mock.NewMockUseCase(controller) + b := entity.NewFixtureBook() manager.EXPECT(). - Search("ozzy"). - Return([]*book.Book{b}, nil) + SearchBooks("ozzy"). + Return([]*entity.Book{b}, nil) ts := httptest.NewServer(listBooks(manager)) defer ts.Close() res, err := http.Get(ts.URL + "?title=ozzy") @@ -73,7 +71,7 @@ func Test_listBooks_Search(t *testing.T) { func Test_createBook(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockManager(controller) + manager := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() MakeBookHandlers(r, *n, manager) @@ -82,7 +80,7 @@ func Test_createBook(t *testing.T) { assert.Equal(t, "/v1/book", path) manager.EXPECT(). - Create(gomock.Any()). + CreateBook(gomock.Any()). Return(entity.NewID(), nil) h := createBook(manager) @@ -97,7 +95,7 @@ func Test_createBook(t *testing.T) { resp, _ := http.Post(ts.URL+"/v1/book", "application/json", strings.NewReader(payload)) assert.Equal(t, http.StatusCreated, resp.StatusCode) - var b *book.Book + var b *entity.Book json.NewDecoder(resp.Body).Decode(&b) assert.Equal(t, "Ozzy Osbourne", b.Author) } @@ -105,16 +103,16 @@ func Test_createBook(t *testing.T) { func Test_getBook(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockManager(controller) + manager := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() MakeBookHandlers(r, *n, manager) path, err := r.GetRoute("getBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book/{id}", path) - b := book.NewFixtureBook() + b := entity.NewFixtureBook() manager.EXPECT(). - Get(b.ID). + GetBook(b.ID). Return(b, nil) handler := getBook(manager) r.Handle("/v1/book/{id}", handler) @@ -123,7 +121,7 @@ func Test_getBook(t *testing.T) { res, err := http.Get(ts.URL + "/v1/book/" + b.ID.String()) assert.Nil(t, err) assert.Equal(t, http.StatusOK, res.StatusCode) - var d *book.Book + var d *entity.Book json.NewDecoder(res.Body).Decode(&d) assert.NotNil(t, d) assert.Equal(t, b.ID, d.ID) @@ -132,15 +130,15 @@ func Test_getBook(t *testing.T) { func Test_deleteBook(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockManager(controller) + manager := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() MakeBookHandlers(r, *n, manager) path, err := r.GetRoute("deleteBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book/{id}", path) - b := book.NewFixtureBook() - manager.EXPECT().Delete(b.ID).Return(nil) + b := entity.NewFixtureBook() + manager.EXPECT().DeleteBook(b.ID).Return(nil) handler := deleteBook(manager) req, _ := http.NewRequest("DELETE", "/v1/bookmark/"+b.ID.String(), nil) r.Handle("/v1/bookmark/{id}", handler).Methods("DELETE", "OPTIONS") diff --git a/api/handler/loan.go b/api/handler/loan.go index 639f7d0..8e655a3 100644 --- a/api/handler/loan.go +++ b/api/handler/loan.go @@ -4,20 +4,19 @@ import ( "fmt" "net/http" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user" "github.com/eminetto/clean-architecture-go-v2/domain/usecase/loan" "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" - "github.com/codegangsta/negroni" + "github.com/eminetto/clean-architecture-go-v2/domain/entity" "github.com/gorilla/mux" ) -func borrowBook(bManager book.Manager, uManager user.Manager, loanUseCase loan.UseCase) http.Handler { +func borrowBook(bookService book.UseCase, userService user.UseCase, loanService loan.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error borrowing book" vars := mux.Vars(r) @@ -27,7 +26,7 @@ func borrowBook(bManager book.Manager, uManager user.Manager, loanUseCase loan.U w.Write([]byte(errorMessage)) return } - b, err := bManager.Get(bID) + b, err := bookService.GetBook(bID) if err != nil && err != domain.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) @@ -44,7 +43,7 @@ func borrowBook(bManager book.Manager, uManager user.Manager, loanUseCase loan.U w.Write([]byte(errorMessage)) return } - u, err := uManager.Get(uID) + u, err := userService.GetUser(uID) if err != nil && err != domain.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) @@ -55,7 +54,7 @@ func borrowBook(bManager book.Manager, uManager user.Manager, loanUseCase loan.U w.Write([]byte(errorMessage)) return } - err = loanUseCase.Borrow(u, b) + err = loanService.Borrow(u, b) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) @@ -66,7 +65,7 @@ func borrowBook(bManager book.Manager, uManager user.Manager, loanUseCase loan.U }) } -func returnBook(bManager book.Manager, loanUseCase loan.UseCase) http.Handler { +func returnBook(bookService book.UseCase, loanService loan.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error returning book" vars := mux.Vars(r) @@ -76,7 +75,7 @@ func returnBook(bManager book.Manager, loanUseCase loan.UseCase) http.Handler { w.Write([]byte(errorMessage)) return } - b, err := bManager.Get(bID) + b, err := bookService.GetBook(bID) if err != nil && err != domain.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) @@ -87,7 +86,7 @@ func returnBook(bManager book.Manager, loanUseCase loan.UseCase) http.Handler { w.Write([]byte(errorMessage)) return } - err = loanUseCase.Return(b) + err = loanService.Return(b) if err != nil && err != domain.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) @@ -98,12 +97,12 @@ func returnBook(bManager book.Manager, loanUseCase loan.UseCase) http.Handler { } //MakeLoanHandlers make url handlers -func MakeLoanHandlers(r *mux.Router, n negroni.Negroni, bManager book.Manager, uManager user.Manager, loanUseCase loan.UseCase) { +func MakeLoanHandlers(r *mux.Router, n negroni.Negroni, bookService book.UseCase, userService user.UseCase, loanService loan.UseCase) { r.Handle("/v1/loan/borrow/{book_id}/{user_id}", n.With( - negroni.Wrap(borrowBook(bManager, uManager, loanUseCase)), + negroni.Wrap(borrowBook(bookService, userService, loanService)), )).Methods("GET", "OPTIONS").Name("borrowBook") r.Handle("/v1/loan/return/{book_id}", n.With( - negroni.Wrap(returnBook(bManager, loanUseCase)), + negroni.Wrap(returnBook(bookService, loanService)), )).Methods("GET", "OPTIONS").Name("returnBook") } diff --git a/api/handler/loan_test.go b/api/handler/loan_test.go index 5f28b61..82b838a 100644 --- a/api/handler/loan_test.go +++ b/api/handler/loan_test.go @@ -6,17 +6,13 @@ import ( "net/http/httptest" "testing" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" - - "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" - "github.com/eminetto/clean-architecture-go-v2/domain" "github.com/eminetto/clean-architecture-go-v2/domain/entity" "github.com/codegangsta/negroni" - bmock "github.com/eminetto/clean-architecture-go-v2/domain/entity/book/mock" - umock "github.com/eminetto/clean-architecture-go-v2/domain/entity/user/mock" + bmock "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book/mock" lmock "github.com/eminetto/clean-architecture-go-v2/domain/usecase/loan/mock" + umock "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user/mock" "github.com/golang/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" @@ -25,8 +21,8 @@ import ( func Test_borrowBook(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - uMock := umock.NewMockManager(controller) - bMock := bmock.NewMockManager(controller) + uMock := umock.NewMockUseCase(controller) + bMock := bmock.NewMockUseCase(controller) lMock := lmock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() @@ -39,7 +35,7 @@ func Test_borrowBook(t *testing.T) { t.Run("book not found", func(t *testing.T) { bID := entity.NewID() uID := entity.NewID() - bMock.EXPECT().Get(bID).Return(nil, domain.ErrNotFound) + bMock.EXPECT().GetBook(bID).Return(nil, domain.ErrNotFound) ts := httptest.NewServer(r) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/v1/loan/borrow/%s/%s", ts.URL, bID.String(), uID.String())) @@ -47,10 +43,10 @@ func Test_borrowBook(t *testing.T) { assert.Equal(t, http.StatusNotFound, res.StatusCode) }) t.Run("user not found", func(t *testing.T) { - b := book.NewFixtureBook() + b := entity.NewFixtureBook() uID := entity.NewID() - bMock.EXPECT().Get(b.ID).Return(b, nil) - uMock.EXPECT().Get(uID).Return(nil, domain.ErrNotFound) + bMock.EXPECT().GetBook(b.ID).Return(b, nil) + uMock.EXPECT().GetUser(uID).Return(nil, domain.ErrNotFound) ts := httptest.NewServer(r) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/v1/loan/borrow/%s/%s", ts.URL, b.ID.String(), uID.String())) @@ -58,10 +54,10 @@ func Test_borrowBook(t *testing.T) { assert.Equal(t, http.StatusNotFound, res.StatusCode) }) t.Run("success", func(t *testing.T) { - b := book.NewFixtureBook() - u := user.NewFixtureUser() - bMock.EXPECT().Get(b.ID).Return(b, nil) - uMock.EXPECT().Get(u.ID).Return(u, nil) + b := entity.NewFixtureBook() + u := entity.NewFixtureUser() + bMock.EXPECT().GetBook(b.ID).Return(b, nil) + uMock.EXPECT().GetUser(u.ID).Return(u, nil) lMock.EXPECT().Borrow(u, b).Return(nil) ts := httptest.NewServer(r) defer ts.Close() @@ -74,8 +70,8 @@ func Test_borrowBook(t *testing.T) { func Test_returnBook(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - uMock := umock.NewMockManager(controller) - bMock := bmock.NewMockManager(controller) + uMock := umock.NewMockUseCase(controller) + bMock := bmock.NewMockUseCase(controller) lMock := lmock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() @@ -87,7 +83,7 @@ func Test_returnBook(t *testing.T) { r.Handle("/v1/loan/return/{book_id}", handler) t.Run("book not found", func(t *testing.T) { bID := entity.NewID() - bMock.EXPECT().Get(bID).Return(nil, domain.ErrNotFound) + bMock.EXPECT().GetBook(bID).Return(nil, domain.ErrNotFound) ts := httptest.NewServer(r) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/v1/loan/return/%s", ts.URL, bID.String())) @@ -95,8 +91,8 @@ func Test_returnBook(t *testing.T) { assert.Equal(t, http.StatusNotFound, res.StatusCode) }) t.Run("success", func(t *testing.T) { - b := book.NewFixtureBook() - bMock.EXPECT().Get(b.ID).Return(b, nil) + b := entity.NewFixtureBook() + bMock.EXPECT().GetBook(b.ID).Return(b, nil) lMock.EXPECT().Return(b).Return(nil) ts := httptest.NewServer(r) defer ts.Close() diff --git a/api/handler/user.go b/api/handler/user.go index 6c7f2a0..f0945c8 100644 --- a/api/handler/user.go +++ b/api/handler/user.go @@ -6,7 +6,7 @@ import ( "net/http" "time" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user" "github.com/eminetto/clean-architecture-go-v2/domain" @@ -18,17 +18,17 @@ import ( "github.com/gorilla/mux" ) -func listUsers(manager user.Manager) http.Handler { +func listUsers(service user.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error reading users" - var data []*user.User + var data []*entity.User var err error name := r.URL.Query().Get("name") switch { case name == "": - data, err = manager.List() + data, err = service.ListUsers() default: - data, err = manager.Search(name) + data, err = service.SearchUsers(name) } w.Header().Set("Content-Type", "application/json") if err != nil && err != domain.ErrNotFound { @@ -58,7 +58,7 @@ func listUsers(manager user.Manager) http.Handler { }) } -func createUser(manager user.Manager) http.Handler { +func createUser(service user.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error adding user" var input struct { @@ -75,7 +75,7 @@ func createUser(manager user.Manager) http.Handler { return } //TODO: validate data ;) - u := &user.User{ + u := &entity.User{ ID: entity.NewID(), Email: input.Email, Password: input.Password, @@ -83,7 +83,7 @@ func createUser(manager user.Manager) http.Handler { LastName: input.LastName, CreatedAt: time.Now(), } - u.ID, err = manager.Create(u) + u.ID, err = service.CreateUser(u) if err != nil { log.Println(err.Error()) w.WriteHeader(http.StatusInternalServerError) @@ -107,7 +107,7 @@ func createUser(manager user.Manager) http.Handler { }) } -func getUser(manager user.Manager) http.Handler { +func getUser(service user.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error reading user" vars := mux.Vars(r) @@ -117,7 +117,7 @@ func getUser(manager user.Manager) http.Handler { w.Write([]byte(errorMessage)) return } - data, err := manager.Get(id) + data, err := service.GetUser(id) w.Header().Set("Content-Type", "application/json") if err != nil && err != domain.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) @@ -143,7 +143,7 @@ func getUser(manager user.Manager) http.Handler { }) } -func deleteUser(manager user.Manager) http.Handler { +func deleteUser(service user.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error removing user" vars := mux.Vars(r) @@ -153,7 +153,7 @@ func deleteUser(manager user.Manager) http.Handler { w.Write([]byte(errorMessage)) return } - err = manager.Delete(id) + err = service.DeleteUser(id) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) @@ -163,20 +163,20 @@ func deleteUser(manager user.Manager) http.Handler { } //MakeUserHandlers make url handlers -func MakeUserHandlers(r *mux.Router, n negroni.Negroni, manager user.Manager) { +func MakeUserHandlers(r *mux.Router, n negroni.Negroni, service user.UseCase) { r.Handle("/v1/user", n.With( - negroni.Wrap(listUsers(manager)), + negroni.Wrap(listUsers(service)), )).Methods("GET", "OPTIONS").Name("listUsers") r.Handle("/v1/user", n.With( - negroni.Wrap(createUser(manager)), + negroni.Wrap(createUser(service)), )).Methods("POST", "OPTIONS").Name("createUser") r.Handle("/v1/user/{id}", n.With( - negroni.Wrap(getUser(manager)), + negroni.Wrap(getUser(service)), )).Methods("GET", "OPTIONS").Name("getUser") r.Handle("/v1/user/{id}", n.With( - negroni.Wrap(deleteUser(manager)), + negroni.Wrap(deleteUser(service)), )).Methods("DELETE", "OPTIONS").Name("deleteUser") } diff --git a/api/handler/user_test.go b/api/handler/user_test.go index 64dcd54..d3d045f 100644 --- a/api/handler/user_test.go +++ b/api/handler/user_test.go @@ -12,8 +12,7 @@ import ( "github.com/eminetto/clean-architecture-go-v2/api/presenter" "github.com/eminetto/clean-architecture-go-v2/domain" "github.com/eminetto/clean-architecture-go-v2/domain/entity" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/user/mock" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user/mock" "github.com/golang/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" @@ -22,17 +21,17 @@ import ( func Test_listUsers(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - m := mock.NewMockManager(controller) + m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() MakeUserHandlers(r, *n, m) path, err := r.GetRoute("listUsers").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user", path) - u := user.NewFixtureUser() + u := entity.NewFixtureUser() m.EXPECT(). - List(). - Return([]*user.User{u}, nil) + ListUsers(). + Return([]*entity.User{u}, nil) ts := httptest.NewServer(listUsers(m)) defer ts.Close() res, err := http.Get(ts.URL) @@ -43,11 +42,11 @@ func Test_listUsers(t *testing.T) { func Test_listUsers_NotFound(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - m := mock.NewMockManager(controller) + m := mock.NewMockUseCase(controller) ts := httptest.NewServer(listUsers(m)) defer ts.Close() m.EXPECT(). - Search("dio"). + SearchUsers("dio"). Return(nil, domain.ErrNotFound) res, err := http.Get(ts.URL + "?name=dio") assert.Nil(t, err) @@ -57,11 +56,11 @@ func Test_listUsers_NotFound(t *testing.T) { func Test_listUsers_Search(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - m := mock.NewMockManager(controller) - u := user.NewFixtureUser() + m := mock.NewMockUseCase(controller) + u := entity.NewFixtureUser() m.EXPECT(). - Search("ozzy"). - Return([]*user.User{u}, nil) + SearchUsers("ozzy"). + Return([]*entity.User{u}, nil) ts := httptest.NewServer(listUsers(m)) defer ts.Close() res, err := http.Get(ts.URL + "?name=ozzy") @@ -72,7 +71,7 @@ func Test_listUsers_Search(t *testing.T) { func Test_createUser(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - m := mock.NewMockManager(controller) + m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() MakeUserHandlers(r, *n, m) @@ -81,7 +80,7 @@ func Test_createUser(t *testing.T) { assert.Equal(t, "/v1/user", path) m.EXPECT(). - Create(gomock.Any()). + CreateUser(gomock.Any()). Return(entity.NewID(), nil) h := createUser(m) @@ -105,16 +104,16 @@ func Test_createUser(t *testing.T) { func Test_getUser(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - m := mock.NewMockManager(controller) + m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() MakeUserHandlers(r, *n, m) path, err := r.GetRoute("getUser").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user/{id}", path) - u := user.NewFixtureUser() + u := entity.NewFixtureUser() m.EXPECT(). - Get(u.ID). + GetUser(u.ID). Return(u, nil) handler := getUser(m) r.Handle("/v1/user/{id}", handler) @@ -132,15 +131,15 @@ func Test_getUser(t *testing.T) { func Test_deleteUser(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - m := mock.NewMockManager(controller) + m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() MakeUserHandlers(r, *n, m) path, err := r.GetRoute("deleteUser").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user/{id}", path) - u := user.NewFixtureUser() - m.EXPECT().Delete(u.ID).Return(nil) + u := entity.NewFixtureUser() + m.EXPECT().DeleteUser(u.ID).Return(nil) handler := deleteUser(m) req, _ := http.NewRequest("DELETE", "/v1/user/"+u.ID.String(), nil) r.Handle("/v1/user/{id}", handler).Methods("DELETE", "OPTIONS") diff --git a/api/main.go b/api/main.go index d040ef1..b659bd0 100644 --- a/api/main.go +++ b/api/main.go @@ -9,13 +9,15 @@ import ( "strconv" "time" + brepo "github.com/eminetto/clean-architecture-go-v2/domain/repository/book" + urepo "github.com/eminetto/clean-architecture-go-v2/domain/repository/user" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user" + "github.com/eminetto/clean-architecture-go-v2/pkg/password" "github.com/eminetto/clean-architecture-go-v2/domain/usecase/loan" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" - - "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/codegangsta/negroni" @@ -37,13 +39,13 @@ func main() { } defer db.Close() - bookRepo := book.NewMySQLRepository(db) - bookManager := book.NewManager(bookRepo) + bookRepo := brepo.NewMySQLRepository(db) + bookManager := book.NewService(bookRepo) - userRepo := user.NewMySQLRepoRepository(db) - userManager := user.NewManager(userRepo, password.NewService()) + userRepo := urepo.NewMySQLRepoRepository(db) + userManager := user.NewService(userRepo, password.NewService()) - loanUseCase := loan.NewUseCase(userManager, bookManager) + loanUseCase := loan.NewService(userManager, bookManager) metricService, err := metric.NewPrometheusService() if err != nil { diff --git a/cmd/main.go b/cmd/main.go index ff2696d..4964e46 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,11 +7,12 @@ import ( "log" "os" + repo "github.com/eminetto/clean-architecture-go-v2/domain/repository/book" + book "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" + "github.com/eminetto/clean-architecture-go-v2/config" _ "github.com/go-sql-driver/mysql" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" - "github.com/eminetto/clean-architecture-go-v2/pkg/metric" ) @@ -40,9 +41,9 @@ func main() { log.Fatal(err.Error()) } defer db.Close() - repo := book.NewMySQLRepository(db) - manager := book.NewManager(repo) - all, err := manager.Search(query) + repo := repo.NewMySQLRepository(db) + manager := book.NewService(repo) + all, err := manager.SearchBooks(query) if err != nil { log.Fatal(err) } diff --git a/domain/entity/book/entity.go b/domain/entity/book.go similarity index 62% rename from domain/entity/book/entity.go rename to domain/entity/book.go index cd6a575..9a5fc0c 100644 --- a/domain/entity/book/entity.go +++ b/domain/entity/book.go @@ -1,14 +1,12 @@ -package book +package entity import ( "time" - - "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) //Book data type Book struct { - ID entity.ID + ID ID Title string Author string Pages int diff --git a/domain/entity/book/interface.go b/domain/entity/book/interface.go deleted file mode 100644 index 233e6cb..0000000 --- a/domain/entity/book/interface.go +++ /dev/null @@ -1,28 +0,0 @@ -package book - -import "github.com/eminetto/clean-architecture-go-v2/domain/entity" - -//Reader interface -type Reader interface { - Get(id entity.ID) (*Book, error) - Search(query string) ([]*Book, error) - List() ([]*Book, error) -} - -//Writer book writer -type Writer interface { - Create(e *Book) (entity.ID, error) - Update(e *Book) error - Delete(id entity.ID) error -} - -//repository interface -type repository interface { - Reader - Writer -} - -//Manager interface -type Manager interface { - repository -} diff --git a/domain/entity/book/manager.go b/domain/entity/book/manager.go deleted file mode 100644 index 893dc2b..0000000 --- a/domain/entity/book/manager.go +++ /dev/null @@ -1,55 +0,0 @@ -package book - -import ( - "strings" - "time" - - "github.com/eminetto/clean-architecture-go-v2/domain/entity" -) - -type manager struct { - repo repository -} - -//NewManager create new manager -func NewManager(r repository) *manager { - return &manager{ - repo: r, - } -} - -//Create a book -func (s *manager) Create(e *Book) (entity.ID, error) { - e.ID = entity.NewID() - e.CreatedAt = time.Now() - return s.repo.Create(e) -} - -//Get a book -func (s *manager) Get(id entity.ID) (*Book, error) { - return s.repo.Get(id) -} - -//Search books -func (s *manager) Search(query string) ([]*Book, error) { - return s.repo.Search(strings.ToLower(query)) -} - -//List books -func (s *manager) List() ([]*Book, error) { - return s.repo.List() -} - -//Delete a book -func (s *manager) Delete(id entity.ID) error { - _, err := s.Get(id) - if err != nil { - return err - } - return s.repo.Delete(id) -} - -//Update a book -func (s *manager) Update(e *Book) error { - return s.repo.Update(e) -} diff --git a/domain/entity/book/mock/book.go b/domain/entity/book/mock/book.go deleted file mode 100644 index e213511..0000000 --- a/domain/entity/book/mock/book.go +++ /dev/null @@ -1,368 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: domain/entity/book/interface.go - -// Package mock is a generated GoMock package. -package mock - -import ( - entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" - book "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" - gomock "github.com/golang/mock/gomock" - reflect "reflect" -) - -// MockReader is a mock of Reader interface -type MockReader struct { - ctrl *gomock.Controller - recorder *MockReaderMockRecorder -} - -// MockReaderMockRecorder is the mock recorder for MockReader -type MockReaderMockRecorder struct { - mock *MockReader -} - -// NewMockReader creates a new mock instance -func NewMockReader(ctrl *gomock.Controller) *MockReader { - mock := &MockReader{ctrl: ctrl} - mock.recorder = &MockReaderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockReader) EXPECT() *MockReaderMockRecorder { - return m.recorder -} - -// Get mocks base method -func (m *MockReader) Get(id entity.ID) (*book.Book, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", id) - ret0, _ := ret[0].(*book.Book) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get -func (mr *MockReaderMockRecorder) Get(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockReader)(nil).Get), id) -} - -// Search mocks base method -func (m *MockReader) Search(query string) ([]*book.Book, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Search", query) - ret0, _ := ret[0].([]*book.Book) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Search indicates an expected call of Search -func (mr *MockReaderMockRecorder) Search(query interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockReader)(nil).Search), query) -} - -// List mocks base method -func (m *MockReader) List() ([]*book.Book, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List") - ret0, _ := ret[0].([]*book.Book) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List -func (mr *MockReaderMockRecorder) List() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockReader)(nil).List)) -} - -// MockWriter is a mock of Writer interface -type MockWriter struct { - ctrl *gomock.Controller - recorder *MockWriterMockRecorder -} - -// MockWriterMockRecorder is the mock recorder for MockWriter -type MockWriterMockRecorder struct { - mock *MockWriter -} - -// NewMockWriter creates a new mock instance -func NewMockWriter(ctrl *gomock.Controller) *MockWriter { - mock := &MockWriter{ctrl: ctrl} - mock.recorder = &MockWriterMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockWriter) EXPECT() *MockWriterMockRecorder { - return m.recorder -} - -// Create mocks base method -func (m *MockWriter) Create(e *book.Book) (entity.ID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", e) - ret0, _ := ret[0].(entity.ID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Create indicates an expected call of Create -func (mr *MockWriterMockRecorder) Create(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockWriter)(nil).Create), e) -} - -// Update mocks base method -func (m *MockWriter) Update(e *book.Book) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", e) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update -func (mr *MockWriterMockRecorder) Update(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockWriter)(nil).Update), e) -} - -// Delete mocks base method -func (m *MockWriter) Delete(id entity.ID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete -func (mr *MockWriterMockRecorder) Delete(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockWriter)(nil).Delete), id) -} - -// MockRepository is a mock of Repository interface -type MockRepository struct { - ctrl *gomock.Controller - recorder *MockRepositoryMockRecorder -} - -// MockRepositoryMockRecorder is the mock recorder for MockRepository -type MockRepositoryMockRecorder struct { - mock *MockRepository -} - -// NewMockRepository creates a new mock instance -func NewMockRepository(ctrl *gomock.Controller) *MockRepository { - mock := &MockRepository{ctrl: ctrl} - mock.recorder = &MockRepositoryMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder { - return m.recorder -} - -// Get mocks base method -func (m *MockRepository) Get(id entity.ID) (*book.Book, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", id) - ret0, _ := ret[0].(*book.Book) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get -func (mr *MockRepositoryMockRecorder) Get(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRepository)(nil).Get), id) -} - -// Search mocks base method -func (m *MockRepository) Search(query string) ([]*book.Book, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Search", query) - ret0, _ := ret[0].([]*book.Book) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Search indicates an expected call of Search -func (mr *MockRepositoryMockRecorder) Search(query interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockRepository)(nil).Search), query) -} - -// List mocks base method -func (m *MockRepository) List() ([]*book.Book, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List") - ret0, _ := ret[0].([]*book.Book) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List -func (mr *MockRepositoryMockRecorder) List() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRepository)(nil).List)) -} - -// Create mocks base method -func (m *MockRepository) Create(e *book.Book) (entity.ID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", e) - ret0, _ := ret[0].(entity.ID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Create indicates an expected call of Create -func (mr *MockRepositoryMockRecorder) Create(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockRepository)(nil).Create), e) -} - -// Update mocks base method -func (m *MockRepository) Update(e *book.Book) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", e) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update -func (mr *MockRepositoryMockRecorder) Update(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockRepository)(nil).Update), e) -} - -// Delete mocks base method -func (m *MockRepository) Delete(id entity.ID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete -func (mr *MockRepositoryMockRecorder) Delete(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockRepository)(nil).Delete), id) -} - -// MockManager is a mock of Manager interface -type MockManager struct { - ctrl *gomock.Controller - recorder *MockManagerMockRecorder -} - -// MockManagerMockRecorder is the mock recorder for MockManager -type MockManagerMockRecorder struct { - mock *MockManager -} - -// NewMockManager creates a new mock instance -func NewMockManager(ctrl *gomock.Controller) *MockManager { - mock := &MockManager{ctrl: ctrl} - mock.recorder = &MockManagerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockManager) EXPECT() *MockManagerMockRecorder { - return m.recorder -} - -// Get mocks base method -func (m *MockManager) Get(id entity.ID) (*book.Book, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", id) - ret0, _ := ret[0].(*book.Book) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get -func (mr *MockManagerMockRecorder) Get(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockManager)(nil).Get), id) -} - -// Search mocks base method -func (m *MockManager) Search(query string) ([]*book.Book, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Search", query) - ret0, _ := ret[0].([]*book.Book) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Search indicates an expected call of Search -func (mr *MockManagerMockRecorder) Search(query interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockManager)(nil).Search), query) -} - -// List mocks base method -func (m *MockManager) List() ([]*book.Book, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List") - ret0, _ := ret[0].([]*book.Book) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List -func (mr *MockManagerMockRecorder) List() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockManager)(nil).List)) -} - -// Create mocks base method -func (m *MockManager) Create(e *book.Book) (entity.ID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", e) - ret0, _ := ret[0].(entity.ID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Create indicates an expected call of Create -func (mr *MockManagerMockRecorder) Create(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockManager)(nil).Create), e) -} - -// Update mocks base method -func (m *MockManager) Update(e *book.Book) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", e) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update -func (mr *MockManagerMockRecorder) Update(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockManager)(nil).Update), e) -} - -// Delete mocks base method -func (m *MockManager) Delete(id entity.ID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete -func (mr *MockManagerMockRecorder) Delete(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockManager)(nil).Delete), id) -} diff --git a/domain/entity/book/fixture.go b/domain/entity/book_fixture.go similarity index 63% rename from domain/entity/book/fixture.go rename to domain/entity/book_fixture.go index 97a3bca..ce7fe1d 100644 --- a/domain/entity/book/fixture.go +++ b/domain/entity/book_fixture.go @@ -1,14 +1,12 @@ -package book +package entity import ( "time" - - "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) func NewFixtureBook() *Book { return &Book{ - ID: entity.NewID(), + ID: NewID(), Title: "I Am Ozzy", Author: "Ozzy Osbourne", Pages: 294, diff --git a/domain/entity/entity.go b/domain/entity/entity.go index 714caf8..f1f0396 100644 --- a/domain/entity/entity.go +++ b/domain/entity/entity.go @@ -12,5 +12,3 @@ func StringToID(s string) (ID, error) { id, err := uuid.Parse(s) return ID(id), err } - - diff --git a/domain/entity/user/entity.go b/domain/entity/user.go similarity index 58% rename from domain/entity/user/entity.go rename to domain/entity/user.go index 5183b01..944debf 100644 --- a/domain/entity/user/entity.go +++ b/domain/entity/user.go @@ -1,19 +1,17 @@ -package user +package entity import ( "time" - - "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) //User data type User struct { - ID entity.ID + ID ID Email string Password string FirstName string LastName string CreatedAt time.Time UpdatedAt time.Time - Books []entity.ID + Books []ID } diff --git a/domain/entity/user/interface.go b/domain/entity/user/interface.go deleted file mode 100644 index edecb79..0000000 --- a/domain/entity/user/interface.go +++ /dev/null @@ -1,28 +0,0 @@ -package user - -import "github.com/eminetto/clean-architecture-go-v2/domain/entity" - -//Reader interface -type Reader interface { - Get(id entity.ID) (*User, error) - Search(query string) ([]*User, error) - List() ([]*User, error) -} - -//Writer user writer -type Writer interface { - Create(e *User) (entity.ID, error) - Update(e *User) error - Delete(id entity.ID) error -} - -//repository interface -type repository interface { - Reader - Writer -} - -//Manager interface -type Manager interface { - repository -} diff --git a/domain/entity/user/mock/user.go b/domain/entity/user/mock/user.go deleted file mode 100644 index 506b675..0000000 --- a/domain/entity/user/mock/user.go +++ /dev/null @@ -1,368 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: domain/entity/user/interface.go - -// Package mock is a generated GoMock package. -package mock - -import ( - entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" - user "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" - gomock "github.com/golang/mock/gomock" - reflect "reflect" -) - -// MockReader is a mock of Reader interface -type MockReader struct { - ctrl *gomock.Controller - recorder *MockReaderMockRecorder -} - -// MockReaderMockRecorder is the mock recorder for MockReader -type MockReaderMockRecorder struct { - mock *MockReader -} - -// NewMockReader creates a new mock instance -func NewMockReader(ctrl *gomock.Controller) *MockReader { - mock := &MockReader{ctrl: ctrl} - mock.recorder = &MockReaderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockReader) EXPECT() *MockReaderMockRecorder { - return m.recorder -} - -// Get mocks base method -func (m *MockReader) Get(id entity.ID) (*user.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", id) - ret0, _ := ret[0].(*user.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get -func (mr *MockReaderMockRecorder) Get(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockReader)(nil).Get), id) -} - -// Search mocks base method -func (m *MockReader) Search(query string) ([]*user.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Search", query) - ret0, _ := ret[0].([]*user.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Search indicates an expected call of Search -func (mr *MockReaderMockRecorder) Search(query interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockReader)(nil).Search), query) -} - -// List mocks base method -func (m *MockReader) List() ([]*user.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List") - ret0, _ := ret[0].([]*user.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List -func (mr *MockReaderMockRecorder) List() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockReader)(nil).List)) -} - -// MockWriter is a mock of Writer interface -type MockWriter struct { - ctrl *gomock.Controller - recorder *MockWriterMockRecorder -} - -// MockWriterMockRecorder is the mock recorder for MockWriter -type MockWriterMockRecorder struct { - mock *MockWriter -} - -// NewMockWriter creates a new mock instance -func NewMockWriter(ctrl *gomock.Controller) *MockWriter { - mock := &MockWriter{ctrl: ctrl} - mock.recorder = &MockWriterMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockWriter) EXPECT() *MockWriterMockRecorder { - return m.recorder -} - -// Create mocks base method -func (m *MockWriter) Create(e *user.User) (entity.ID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", e) - ret0, _ := ret[0].(entity.ID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Create indicates an expected call of Create -func (mr *MockWriterMockRecorder) Create(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockWriter)(nil).Create), e) -} - -// Update mocks base method -func (m *MockWriter) Update(e *user.User) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", e) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update -func (mr *MockWriterMockRecorder) Update(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockWriter)(nil).Update), e) -} - -// Delete mocks base method -func (m *MockWriter) Delete(id entity.ID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete -func (mr *MockWriterMockRecorder) Delete(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockWriter)(nil).Delete), id) -} - -// MockRepository is a mock of Repository interface -type MockRepository struct { - ctrl *gomock.Controller - recorder *MockRepositoryMockRecorder -} - -// MockRepositoryMockRecorder is the mock recorder for MockRepository -type MockRepositoryMockRecorder struct { - mock *MockRepository -} - -// NewMockRepository creates a new mock instance -func NewMockRepository(ctrl *gomock.Controller) *MockRepository { - mock := &MockRepository{ctrl: ctrl} - mock.recorder = &MockRepositoryMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder { - return m.recorder -} - -// Get mocks base method -func (m *MockRepository) Get(id entity.ID) (*user.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", id) - ret0, _ := ret[0].(*user.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get -func (mr *MockRepositoryMockRecorder) Get(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRepository)(nil).Get), id) -} - -// Search mocks base method -func (m *MockRepository) Search(query string) ([]*user.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Search", query) - ret0, _ := ret[0].([]*user.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Search indicates an expected call of Search -func (mr *MockRepositoryMockRecorder) Search(query interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockRepository)(nil).Search), query) -} - -// List mocks base method -func (m *MockRepository) List() ([]*user.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List") - ret0, _ := ret[0].([]*user.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List -func (mr *MockRepositoryMockRecorder) List() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRepository)(nil).List)) -} - -// Create mocks base method -func (m *MockRepository) Create(e *user.User) (entity.ID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", e) - ret0, _ := ret[0].(entity.ID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Create indicates an expected call of Create -func (mr *MockRepositoryMockRecorder) Create(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockRepository)(nil).Create), e) -} - -// Update mocks base method -func (m *MockRepository) Update(e *user.User) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", e) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update -func (mr *MockRepositoryMockRecorder) Update(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockRepository)(nil).Update), e) -} - -// Delete mocks base method -func (m *MockRepository) Delete(id entity.ID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete -func (mr *MockRepositoryMockRecorder) Delete(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockRepository)(nil).Delete), id) -} - -// MockManager is a mock of Manager interface -type MockManager struct { - ctrl *gomock.Controller - recorder *MockManagerMockRecorder -} - -// MockManagerMockRecorder is the mock recorder for MockManager -type MockManagerMockRecorder struct { - mock *MockManager -} - -// NewMockManager creates a new mock instance -func NewMockManager(ctrl *gomock.Controller) *MockManager { - mock := &MockManager{ctrl: ctrl} - mock.recorder = &MockManagerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockManager) EXPECT() *MockManagerMockRecorder { - return m.recorder -} - -// Get mocks base method -func (m *MockManager) Get(id entity.ID) (*user.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", id) - ret0, _ := ret[0].(*user.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get -func (mr *MockManagerMockRecorder) Get(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockManager)(nil).Get), id) -} - -// Search mocks base method -func (m *MockManager) Search(query string) ([]*user.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Search", query) - ret0, _ := ret[0].([]*user.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Search indicates an expected call of Search -func (mr *MockManagerMockRecorder) Search(query interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockManager)(nil).Search), query) -} - -// List mocks base method -func (m *MockManager) List() ([]*user.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List") - ret0, _ := ret[0].([]*user.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// List indicates an expected call of List -func (mr *MockManagerMockRecorder) List() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockManager)(nil).List)) -} - -// Create mocks base method -func (m *MockManager) Create(e *user.User) (entity.ID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", e) - ret0, _ := ret[0].(entity.ID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Create indicates an expected call of Create -func (mr *MockManagerMockRecorder) Create(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockManager)(nil).Create), e) -} - -// Update mocks base method -func (m *MockManager) Update(e *user.User) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Update", e) - ret0, _ := ret[0].(error) - return ret0 -} - -// Update indicates an expected call of Update -func (mr *MockManagerMockRecorder) Update(e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockManager)(nil).Update), e) -} - -// Delete mocks base method -func (m *MockManager) Delete(id entity.ID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", id) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete -func (mr *MockManagerMockRecorder) Delete(id interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockManager)(nil).Delete), id) -} diff --git a/domain/entity/user/fixture.go b/domain/entity/user_fixture.go similarity index 65% rename from domain/entity/user/fixture.go rename to domain/entity/user_fixture.go index 2b776c5..d83f996 100644 --- a/domain/entity/user/fixture.go +++ b/domain/entity/user_fixture.go @@ -1,14 +1,12 @@ -package user +package entity import ( "time" - - "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) func NewFixtureUser() *User { return &User{ - ID: entity.NewID(), + ID: NewID(), Email: "ozzy@metalgods.net", Password: "123456", FirstName: "Ozzy", diff --git a/domain/repository/book/interface.go b/domain/repository/book/interface.go new file mode 100644 index 0000000..8a1fa36 --- /dev/null +++ b/domain/repository/book/interface.go @@ -0,0 +1,25 @@ +package book + +import ( + "github.com/eminetto/clean-architecture-go-v2/domain/entity" +) + +//Reader interface +type Reader interface { + Get(id entity.ID) (*entity.Book, error) + Search(query string) ([]*entity.Book, error) + List() ([]*entity.Book, error) +} + +//Writer book writer +type Writer interface { + Create(e *entity.Book) (entity.ID, error) + Update(e *entity.Book) error + Delete(id entity.ID) error +} + +//repository interface +type Repository interface { + Reader + Writer +} diff --git a/domain/entity/book/repository_inmem.go b/domain/repository/book/repository_inmem.go similarity index 54% rename from domain/entity/book/repository_inmem.go rename to domain/repository/book/repository_inmem.go index 1410bea..7714bf2 100644 --- a/domain/entity/book/repository_inmem.go +++ b/domain/repository/book/repository_inmem.go @@ -7,35 +7,36 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) -//iRepo in memory repo -type iRepo struct { - m map[entity.ID]*Book +//InmemRepo in memory repo +type InmemRepo struct { + m map[entity.ID]*entity.Book } //NewInmemRepository create new repository -func NewInmemRepository() *iRepo { - var m = map[entity.ID]*Book{} - return &iRepo{ +func NewInmemRepository() *InmemRepo { + var m = map[entity.ID]*entity.Book{} + return &InmemRepo{ m: m, } } //Create a book -func (r *iRepo) Create(e *Book) (entity.ID, error) { +func (r *InmemRepo) Create(e *entity.Book) (entity.ID, error) { r.m[e.ID] = e return e.ID, nil } //Get a book -func (r *iRepo) Get(id entity.ID) (*Book, error) { +func (r *InmemRepo) Get(id entity.ID) (*entity.Book, error) { if r.m[id] == nil { + // return nil, fmt.Errorf("not found") return nil, domain.ErrNotFound } return r.m[id], nil } //Update a book -func (r *iRepo) Update(e *Book) error { +func (r *InmemRepo) Update(e *entity.Book) error { _, err := r.Get(e.ID) if err != nil { return err @@ -45,23 +46,19 @@ func (r *iRepo) Update(e *Book) error { } //Search books -func (r *iRepo) Search(query string) ([]*Book, error) { - var d []*Book +func (r *InmemRepo) Search(query string) ([]*entity.Book, error) { + var d []*entity.Book for _, j := range r.m { if strings.Contains(strings.ToLower(j.Title), query) { d = append(d, j) } } - if len(d) == 0 { - return nil, domain.ErrNotFound - } - return d, nil } //List books -func (r *iRepo) List() ([]*Book, error) { - var d []*Book +func (r *InmemRepo) List() ([]*entity.Book, error) { + var d []*entity.Book for _, j := range r.m { d = append(d, j) } @@ -69,8 +66,9 @@ func (r *iRepo) List() ([]*Book, error) { } //Delete a book -func (r *iRepo) Delete(id entity.ID) error { +func (r *InmemRepo) Delete(id entity.ID) error { if r.m[id] == nil { + // return fmt.Errorf("not found") return domain.ErrNotFound } r.m[id] = nil diff --git a/domain/entity/book/repository_mysql.go b/domain/repository/book/repository_mysql.go similarity index 76% rename from domain/entity/book/repository_mysql.go rename to domain/repository/book/repository_mysql.go index c46f051..f7945cf 100644 --- a/domain/entity/book/repository_mysql.go +++ b/domain/repository/book/repository_mysql.go @@ -4,25 +4,23 @@ import ( "database/sql" "time" - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) -//mySQLRepo mysql repo -type mySQLRepo struct { +//MySQLRepo mysql repo +type MySQLRepo struct { db *sql.DB } //NewMySQLRepository create new repository -func NewMySQLRepository(db *sql.DB) *mySQLRepo { - return &mySQLRepo{ +func NewMySQLRepository(db *sql.DB) *MySQLRepo { + return &MySQLRepo{ db: db, } } //Create a book -func (r *mySQLRepo) Create(e *Book) (entity.ID, error) { +func (r *MySQLRepo) Create(e *entity.Book) (entity.ID, error) { stmt, err := r.db.Prepare(` insert into book (id, title, author, pages, quantity, created_at) values(?,?,?,?,?,?)`) @@ -48,12 +46,12 @@ func (r *mySQLRepo) Create(e *Book) (entity.ID, error) { } //Get a book -func (r *mySQLRepo) Get(id entity.ID) (*Book, error) { +func (r *MySQLRepo) Get(id entity.ID) (*entity.Book, error) { stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book where id = ?`) if err != nil { return nil, err } - var b Book + var b entity.Book rows, err := stmt.Query(id) if err != nil { return nil, err @@ -65,7 +63,7 @@ func (r *mySQLRepo) Get(id entity.ID) (*Book, error) { } //Update a book -func (r *mySQLRepo) Update(e *Book) error { +func (r *MySQLRepo) Update(e *entity.Book) error { e.UpdatedAt = time.Now() _, err := r.db.Exec("update book set title = ?, author = ?, pages = ?, quantity = ?, updated_at = ? where id = ?", e.Title, e.Author, e.Pages, e.Quantity, e.UpdatedAt.Format("2006-01-02"), e.ID) if err != nil { @@ -75,57 +73,52 @@ func (r *mySQLRepo) Update(e *Book) error { } //Search books -func (r *mySQLRepo) Search(query string) ([]*Book, error) { +func (r *MySQLRepo) Search(query string) ([]*entity.Book, error) { stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book where title like ?`) if err != nil { return nil, err } - var books []*Book + var books []*entity.Book rows, err := stmt.Query("%" + query + "%") if err != nil { return nil, err } for rows.Next() { - var b Book + var b entity.Book err = rows.Scan(&b.ID, &b.Title, &b.Author, &b.Pages, &b.Quantity, &b.CreatedAt) if err != nil { return nil, err } books = append(books, &b) } - if len(books) == 0 { - return nil, domain.ErrNotFound - } + return books, nil } //List books -func (r *mySQLRepo) List() ([]*Book, error) { +func (r *MySQLRepo) List() ([]*entity.Book, error) { stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book`) if err != nil { return nil, err } - var books []*Book + var books []*entity.Book rows, err := stmt.Query() if err != nil { return nil, err } for rows.Next() { - var b Book + var b entity.Book err = rows.Scan(&b.ID, &b.Title, &b.Author, &b.Pages, &b.Quantity, &b.CreatedAt) if err != nil { return nil, err } books = append(books, &b) } - if len(books) == 0 { - return nil, domain.ErrNotFound - } return books, nil } //Delete a book -func (r *mySQLRepo) Delete(id entity.ID) error { +func (r *MySQLRepo) Delete(id entity.ID) error { _, err := r.db.Exec("delete from book where id = ?", id) if err != nil { return err diff --git a/domain/repository/user/interface.go b/domain/repository/user/interface.go new file mode 100644 index 0000000..8180476 --- /dev/null +++ b/domain/repository/user/interface.go @@ -0,0 +1,25 @@ +package user + +import ( + "github.com/eminetto/clean-architecture-go-v2/domain/entity" +) + +//Reader interface +type Reader interface { + Get(id entity.ID) (*entity.User, error) + Search(query string) ([]*entity.User, error) + List() ([]*entity.User, error) +} + +//Writer user writer +type Writer interface { + Create(e *entity.User) (entity.ID, error) + Update(e *entity.User) error + Delete(id entity.ID) error +} + +//Repository interface +type Repository interface { + Reader + Writer +} diff --git a/domain/entity/user/repository_inmem.go b/domain/repository/user/repository_inmem.go similarity index 65% rename from domain/entity/user/repository_inmem.go rename to domain/repository/user/repository_inmem.go index 977689e..eb746bf 100644 --- a/domain/entity/user/repository_inmem.go +++ b/domain/repository/user/repository_inmem.go @@ -1,6 +1,7 @@ package user import ( + "fmt" "strings" "github.com/eminetto/clean-architecture-go-v2/domain" @@ -9,33 +10,34 @@ import ( //IRepo in memory repo type IRepo struct { - m map[entity.ID]*User + m map[entity.ID]*entity.User } //NewInmemRepository create new repository func NewInmemRepository() *IRepo { - var m = map[entity.ID]*User{} + var m = map[entity.ID]*entity.User{} return &IRepo{ m: m, } } //Create an user -func (r *IRepo) Create(e *User) (entity.ID, error) { +func (r *IRepo) Create(e *entity.User) (entity.ID, error) { r.m[e.ID] = e return e.ID, nil } //Get an user -func (r *IRepo) Get(id entity.ID) (*User, error) { +func (r *IRepo) Get(id entity.ID) (*entity.User, error) { if r.m[id] == nil { + // return nil, fmt.Errorf("not found") return nil, domain.ErrNotFound } return r.m[id], nil } //Update an user -func (r *IRepo) Update(e *User) error { +func (r *IRepo) Update(e *entity.User) error { _, err := r.Get(e.ID) if err != nil { return err @@ -45,14 +47,15 @@ func (r *IRepo) Update(e *User) error { } //Search users -func (r *IRepo) Search(query string) ([]*User, error) { - var d []*User +func (r *IRepo) Search(query string) ([]*entity.User, error) { + var d []*entity.User for _, j := range r.m { if strings.Contains(strings.ToLower(j.FirstName), query) { d = append(d, j) } } if len(d) == 0 { + // return nil, fmt.Errorf("not found") return nil, domain.ErrNotFound } @@ -60,8 +63,8 @@ func (r *IRepo) Search(query string) ([]*User, error) { } //List users -func (r *IRepo) List() ([]*User, error) { - var d []*User +func (r *IRepo) List() ([]*entity.User, error) { + var d []*entity.User for _, j := range r.m { d = append(d, j) } @@ -71,7 +74,7 @@ func (r *IRepo) List() ([]*User, error) { //Delete an user func (r *IRepo) Delete(id entity.ID) error { if r.m[id] == nil { - return domain.ErrNotFound + return fmt.Errorf("not found") } r.m[id] = nil return nil diff --git a/domain/entity/user/repository_mysql.go b/domain/repository/user/repository_mysql.go similarity index 85% rename from domain/entity/user/repository_mysql.go rename to domain/repository/user/repository_mysql.go index d9ac700..41882fc 100644 --- a/domain/entity/user/repository_mysql.go +++ b/domain/repository/user/repository_mysql.go @@ -2,10 +2,9 @@ package user import ( "database/sql" + "fmt" "time" - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) @@ -22,7 +21,7 @@ func NewMySQLRepoRepository(db *sql.DB) *MySQLRepo { } //Create an user -func (r *MySQLRepo) Create(e *User) (entity.ID, error) { +func (r *MySQLRepo) Create(e *entity.User) (entity.ID, error) { stmt, err := r.db.Prepare(` insert into user (id, email, password, first_name, last_name, created_at) values(?,?,?,?,?,?)`) @@ -48,16 +47,16 @@ func (r *MySQLRepo) Create(e *User) (entity.ID, error) { } //Get an user -func (r *MySQLRepo) Get(id entity.ID) (*User, error) { +func (r *MySQLRepo) Get(id entity.ID) (*entity.User, error) { return getUser(id, r.db) } -func getUser(id entity.ID, db *sql.DB) (*User, error) { +func getUser(id entity.ID, db *sql.DB) (*entity.User, error) { stmt, err := db.Prepare(`select id, email, first_name, last_name, created_at from user where id = ?`) if err != nil { return nil, err } - var u User + var u entity.User rows, err := stmt.Query(id) if err != nil { return nil, err @@ -82,7 +81,7 @@ func getUser(id entity.ID, db *sql.DB) (*User, error) { } //Update an user -func (r *MySQLRepo) Update(e *User) error { +func (r *MySQLRepo) Update(e *entity.User) error { e.UpdatedAt = time.Now() _, err := r.db.Exec("update user set email = ?, password = ?, first_name = ?, last_name = ?, updated_at = ? where id = ?", e.Email, e.Password, e.FirstName, e.LastName, e.UpdatedAt.Format("2006-01-02"), e.ID) if err != nil { @@ -102,7 +101,7 @@ func (r *MySQLRepo) Update(e *User) error { } //Search users -func (r *MySQLRepo) Search(query string) ([]*User, error) { +func (r *MySQLRepo) Search(query string) ([]*entity.User, error) { stmt, err := r.db.Prepare(`select id from user where name like ?`) if err != nil { return nil, err @@ -122,9 +121,9 @@ func (r *MySQLRepo) Search(query string) ([]*User, error) { ids = append(ids, i) } if len(ids) == 0 { - return nil, domain.ErrNotFound + return nil, fmt.Errorf("not found") } - var users []*User + var users []*entity.User for _, id := range ids { u, err := getUser(id, r.db) if err != nil { @@ -136,7 +135,7 @@ func (r *MySQLRepo) Search(query string) ([]*User, error) { } //List users -func (r *MySQLRepo) List() ([]*User, error) { +func (r *MySQLRepo) List() ([]*entity.User, error) { stmt, err := r.db.Prepare(`select id from user`) if err != nil { return nil, err @@ -156,9 +155,9 @@ func (r *MySQLRepo) List() ([]*User, error) { ids = append(ids, i) } if len(ids) == 0 { - return nil, domain.ErrNotFound + return nil, fmt.Errorf("not found") } - var users []*User + var users []*entity.User for _, id := range ids { u, err := getUser(id, r.db) if err != nil { diff --git a/domain/usecase/book/interface.go b/domain/usecase/book/interface.go new file mode 100644 index 0000000..a9bf4a6 --- /dev/null +++ b/domain/usecase/book/interface.go @@ -0,0 +1,15 @@ +package book + +import ( + "github.com/eminetto/clean-architecture-go-v2/domain/entity" +) + +//UseCase interface +type UseCase interface { + GetBook(id entity.ID) (*entity.Book, error) + SearchBooks(query string) ([]*entity.Book, error) + ListBooks() ([]*entity.Book, error) + CreateBook(e *entity.Book) (entity.ID, error) + UpdateBook(e *entity.Book) error + DeleteBook(id entity.ID) error +} diff --git a/domain/usecase/book/mock/book.go b/domain/usecase/book/mock/book.go new file mode 100644 index 0000000..9686c0f --- /dev/null +++ b/domain/usecase/book/mock/book.go @@ -0,0 +1,123 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: domain/usecase/book/interface.go + +// Package mock is a generated GoMock package. +package mock + +import ( + reflect "reflect" + + entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" + gomock "github.com/golang/mock/gomock" +) + +// MockUseCase is a mock of UseCase interface +type MockUseCase struct { + ctrl *gomock.Controller + recorder *MockUseCaseMockRecorder +} + +// MockUseCaseMockRecorder is the mock recorder for MockUseCase +type MockUseCaseMockRecorder struct { + mock *MockUseCase +} + +// NewMockUseCase creates a new mock instance +func NewMockUseCase(ctrl *gomock.Controller) *MockUseCase { + mock := &MockUseCase{ctrl: ctrl} + mock.recorder = &MockUseCaseMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockUseCase) EXPECT() *MockUseCaseMockRecorder { + return m.recorder +} + +// GetBook mocks base method +func (m *MockUseCase) GetBook(id entity.ID) (*entity.Book, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBook", id) + ret0, _ := ret[0].(*entity.Book) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBook indicates an expected call of GetBook +func (mr *MockUseCaseMockRecorder) GetBook(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBook", reflect.TypeOf((*MockUseCase)(nil).GetBook), id) +} + +// SearchBooks mocks base method +func (m *MockUseCase) SearchBooks(query string) ([]*entity.Book, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SearchBooks", query) + ret0, _ := ret[0].([]*entity.Book) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SearchBooks indicates an expected call of SearchBooks +func (mr *MockUseCaseMockRecorder) SearchBooks(query interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchBooks", reflect.TypeOf((*MockUseCase)(nil).SearchBooks), query) +} + +// ListBooks mocks base method +func (m *MockUseCase) ListBooks() ([]*entity.Book, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListBooks") + ret0, _ := ret[0].([]*entity.Book) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListBooks indicates an expected call of ListBooks +func (mr *MockUseCaseMockRecorder) ListBooks() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBooks", reflect.TypeOf((*MockUseCase)(nil).ListBooks)) +} + +// CreateBook mocks base method +func (m *MockUseCase) CreateBook(e *entity.Book) (entity.ID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateBook", e) + ret0, _ := ret[0].(entity.ID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateBook indicates an expected call of CreateBook +func (mr *MockUseCaseMockRecorder) CreateBook(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBook", reflect.TypeOf((*MockUseCase)(nil).CreateBook), e) +} + +// UpdateBook mocks base method +func (m *MockUseCase) UpdateBook(e *entity.Book) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateBook", e) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateBook indicates an expected call of UpdateBook +func (mr *MockUseCaseMockRecorder) UpdateBook(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateBook", reflect.TypeOf((*MockUseCase)(nil).UpdateBook), e) +} + +// DeleteBook mocks base method +func (m *MockUseCase) DeleteBook(id entity.ID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteBook", id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteBook indicates an expected call of DeleteBook +func (mr *MockUseCaseMockRecorder) DeleteBook(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBook", reflect.TypeOf((*MockUseCase)(nil).DeleteBook), id) +} diff --git a/domain/usecase/book/service.go b/domain/usecase/book/service.go new file mode 100644 index 0000000..adf5486 --- /dev/null +++ b/domain/usecase/book/service.go @@ -0,0 +1,82 @@ +package book + +import ( + "strings" + "time" + + repo "github.com/eminetto/clean-architecture-go-v2/domain/repository/book" + + "github.com/eminetto/clean-architecture-go-v2/domain" + + "github.com/eminetto/clean-architecture-go-v2/domain/entity" +) + +//Service book usecase +type Service struct { + repo repo.Repository +} + +//NewService create new service +func NewService(r repo.Repository) *Service { + return &Service{ + repo: r, + } +} + +//Create a book +func (s *Service) CreateBook(e *entity.Book) (entity.ID, error) { + e.ID = entity.NewID() + e.CreatedAt = time.Now() + return s.repo.Create(e) +} + +//Get a book +func (s *Service) GetBook(id entity.ID) (*entity.Book, error) { + b, err := s.repo.Get(id) + if b == nil { + return nil, domain.ErrNotFound + } + if err != nil { + return nil, err + } + + return b, nil +} + +//Search books +func (s *Service) SearchBooks(query string) ([]*entity.Book, error) { + books, err := s.repo.Search(strings.ToLower(query)) + if err != nil { + return nil, err + } + if len(books) == 0 { + return nil, domain.ErrNotFound + } + return books, nil +} + +//List books +func (s *Service) ListBooks() ([]*entity.Book, error) { + books, err := s.repo.List() + if err != nil { + return nil, err + } + if len(books) == 0 { + return nil, domain.ErrNotFound + } + return books, nil +} + +//Delete a book +func (s *Service) DeleteBook(id entity.ID) error { + _, err := s.GetBook(id) + if err != nil { + return err + } + return s.repo.Delete(id) +} + +//Update a book +func (s *Service) UpdateBook(e *entity.Book) error { + return s.repo.Update(e) +} diff --git a/domain/entity/book/manager_test.go b/domain/usecase/book/service_test.go similarity index 50% rename from domain/entity/book/manager_test.go rename to domain/usecase/book/service_test.go index 2249154..803292d 100644 --- a/domain/entity/book/manager_test.go +++ b/domain/usecase/book/service_test.go @@ -3,80 +3,84 @@ package book import ( "testing" + "github.com/eminetto/clean-architecture-go-v2/domain/entity" + + "github.com/eminetto/clean-architecture-go-v2/domain/repository/book" + "github.com/eminetto/clean-architecture-go-v2/domain" "github.com/stretchr/testify/assert" ) func Test_Create(t *testing.T) { - repo := NewInmemRepository() - m := NewManager(repo) - u := NewFixtureBook() - id, err := m.Create(u) + repo := book.NewInmemRepository() + m := NewService(repo) + u := entity.NewFixtureBook() + id, err := m.CreateBook(u) assert.Nil(t, err) assert.Equal(t, u.ID, id) assert.False(t, u.CreatedAt.IsZero()) } func Test_SearchAndFind(t *testing.T) { - repo := NewInmemRepository() - m := NewManager(repo) - u1 := NewFixtureBook() - u2 := NewFixtureBook() + repo := book.NewInmemRepository() + m := NewService(repo) + u1 := entity.NewFixtureBook() + u2 := entity.NewFixtureBook() u2.Title = "Lemmy: Biography" - uID, _ := m.Create(u1) - _, _ = m.Create(u2) + uID, _ := m.CreateBook(u1) + _, _ = m.CreateBook(u2) t.Run("search", func(t *testing.T) { - c, err := m.Search("ozzy") + c, err := m.SearchBooks("ozzy") assert.Nil(t, err) assert.Equal(t, 1, len(c)) assert.Equal(t, "I Am Ozzy", c[0].Title) - c, err = m.Search("dio") + c, err = m.SearchBooks("dio") assert.Equal(t, domain.ErrNotFound, err) assert.Nil(t, c) }) t.Run("list all", func(t *testing.T) { - all, err := m.List() + all, err := m.ListBooks() assert.Nil(t, err) assert.Equal(t, 2, len(all)) }) t.Run("get", func(t *testing.T) { - saved, err := m.Get(uID) + saved, err := m.GetBook(uID) assert.Nil(t, err) assert.Equal(t, u1.Title, saved.Title) }) } func Test_Update(t *testing.T) { - repo := NewInmemRepository() - m := NewManager(repo) - u := NewFixtureBook() - id, err := m.Create(u) + repo := book.NewInmemRepository() + m := NewService(repo) + u := entity.NewFixtureBook() + id, err := m.CreateBook(u) assert.Nil(t, err) - saved, _ := m.Get(id) + saved, _ := m.GetBook(id) saved.Title = "Lemmy: Biography" - assert.Nil(t, m.Update(saved)) - updated, err := m.Get(id) + assert.Nil(t, m.UpdateBook(saved)) + updated, err := m.GetBook(id) assert.Nil(t, err) assert.Equal(t, "Lemmy: Biography", updated.Title) } func TestDelete(t *testing.T) { - repo := NewInmemRepository() - m := NewManager(repo) - u1 := NewFixtureBook() - u2 := NewFixtureBook() - u2ID, _ := m.Create(u2) + repo := book.NewInmemRepository() + m := NewService(repo) + u1 := entity.NewFixtureBook() + u2 := entity.NewFixtureBook() + u2ID, _ := m.CreateBook(u2) - err := m.Delete(u1.ID) + err := m.DeleteBook(u1.ID) assert.Equal(t, domain.ErrNotFound, err) - err = m.Delete(u2ID) + err = m.DeleteBook(u2ID) assert.Nil(t, err) - _, err = m.Get(u2ID) + _, err = m.GetBook(u2ID) assert.Equal(t, domain.ErrNotFound, err) } diff --git a/domain/usecase/loan/interface.go b/domain/usecase/loan/interface.go index 1ad50b2..6bbf546 100644 --- a/domain/usecase/loan/interface.go +++ b/domain/usecase/loan/interface.go @@ -1,12 +1,11 @@ package loan import ( - "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" + "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) //UseCase use case interface type UseCase interface { - Borrow(u *user.User, b *book.Book) error - Return(b *book.Book) error + Borrow(u *entity.User, b *entity.Book) error + Return(b *entity.Book) error } diff --git a/domain/usecase/loan/mock/loan.go b/domain/usecase/loan/mock/loan.go index ce46511..5d5b355 100644 --- a/domain/usecase/loan/mock/loan.go +++ b/domain/usecase/loan/mock/loan.go @@ -5,10 +5,10 @@ package mock import ( - book "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" - user "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" - gomock "github.com/golang/mock/gomock" reflect "reflect" + + entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" + gomock "github.com/golang/mock/gomock" ) // MockUseCase is a mock of UseCase interface @@ -35,7 +35,7 @@ func (m *MockUseCase) EXPECT() *MockUseCaseMockRecorder { } // Borrow mocks base method -func (m *MockUseCase) Borrow(u *user.User, b *book.Book) error { +func (m *MockUseCase) Borrow(u *entity.User, b *entity.Book) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Borrow", u, b) ret0, _ := ret[0].(error) @@ -49,7 +49,7 @@ func (mr *MockUseCaseMockRecorder) Borrow(u, b interface{}) *gomock.Call { } // Return mocks base method -func (m *MockUseCase) Return(b *book.Book) error { +func (m *MockUseCase) Return(b *entity.Book) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Return", b) ret0, _ := ret[0].(error) diff --git a/domain/usecase/loan/usecase.go b/domain/usecase/loan/service.go similarity index 57% rename from domain/usecase/loan/usecase.go rename to domain/usecase/loan/service.go index 7daa7d0..18e684a 100644 --- a/domain/usecase/loan/usecase.go +++ b/domain/usecase/loan/service.go @@ -3,30 +3,31 @@ package loan import ( "github.com/eminetto/clean-architecture-go-v2/domain" "github.com/eminetto/clean-architecture-go-v2/domain/entity" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user" ) -type usecase struct { - uManager user.Manager - bManager book.Manager +//Service loan usecase +type Service struct { + userService user.UseCase + bookService book.UseCase } -//NewUseCase create new use case -func NewUseCase(u user.Manager, b book.Manager) *usecase { - return &usecase{ - uManager: u, - bManager: b, +//NewService create new use case +func NewService(u user.UseCase, b book.UseCase) *Service { + return &Service{ + userService: u, + bookService: b, } } //Borrow borrow a book to an user -func (s *usecase) Borrow(u *user.User, b *book.Book) error { - u, err := s.uManager.Get(u.ID) +func (s *Service) Borrow(u *entity.User, b *entity.Book) error { + u, err := s.userService.GetUser(u.ID) if err != nil { return err } - b, err = s.bManager.Get(b.ID) + b, err = s.bookService.GetBook(b.ID) if err != nil { return err } @@ -39,12 +40,12 @@ func (s *usecase) Borrow(u *user.User, b *book.Book) error { } } u.Books = append(u.Books, b.ID) - err = s.uManager.Update(u) + err = s.userService.UpdateUser(u) if err != nil { return err } b.Quantity-- - err = s.bManager.Update(b) + err = s.bookService.UpdateBook(b) if err != nil { return err } @@ -52,13 +53,13 @@ func (s *usecase) Borrow(u *user.User, b *book.Book) error { } //Return return a book -func (s *usecase) Return(b *book.Book) error { - b, err := s.bManager.Get(b.ID) +func (s *Service) Return(b *entity.Book) error { + b, err := s.bookService.GetBook(b.ID) if err != nil { return err } - all, err := s.uManager.List() + all, err := s.userService.ListUsers() if err != nil { return err } @@ -76,14 +77,14 @@ func (s *usecase) Return(b *book.Book) error { if !borrowed { return domain.ErrBookNotBorrowed } - u, err := s.uManager.Get(borrowedBy) + u, err := s.userService.GetUser(borrowedBy) if err != nil { return err } for i, j := range u.Books { if j == b.ID { u.Books = append(u.Books[:i], u.Books[i+1:]...) - err = s.uManager.Update(u) + err = s.userService.UpdateUser(u) if err != nil { return err } @@ -91,7 +92,7 @@ func (s *usecase) Return(b *book.Book) error { } } b.Quantity++ - err = s.bManager.Update(b) + err = s.bookService.UpdateBook(b) if err != nil { return err } diff --git a/domain/usecase/loan/service_test.go b/domain/usecase/loan/service_test.go new file mode 100644 index 0000000..663d7ba --- /dev/null +++ b/domain/usecase/loan/service_test.go @@ -0,0 +1,99 @@ +package loan + +import ( + "testing" + + "github.com/eminetto/clean-architecture-go-v2/domain/entity" + + "github.com/eminetto/clean-architecture-go-v2/domain" + bmock "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book/mock" + umock "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user/mock" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +func Test_Borrow(t *testing.T) { + controller := gomock.NewController(t) + defer controller.Finish() + uMock := umock.NewMockUseCase(controller) + bMock := bmock.NewMockUseCase(controller) + uc := NewService(uMock, bMock) + t.Run("user not found", func(t *testing.T) { + u := entity.NewFixtureUser() + b := entity.NewFixtureBook() + uMock.EXPECT().GetUser(u.ID).Return(nil, domain.ErrNotFound) + err := uc.Borrow(u, b) + assert.Equal(t, domain.ErrNotFound, err) + }) + t.Run("book not found", func(t *testing.T) { + u := entity.NewFixtureUser() + b := entity.NewFixtureBook() + uMock.EXPECT().GetUser(u.ID).Return(u, nil) + bMock.EXPECT().GetBook(b.ID).Return(nil, domain.ErrNotFound) + err := uc.Borrow(u, b) + assert.Equal(t, domain.ErrNotFound, err) + }) + t.Run("not enough books to borrow", func(t *testing.T) { + u := entity.NewFixtureUser() + b := entity.NewFixtureBook() + b.Quantity = 0 + uMock.EXPECT().GetUser(u.ID).Return(u, nil) + bMock.EXPECT().GetBook(b.ID).Return(b, nil) + err := uc.Borrow(u, b) + assert.Equal(t, domain.ErrNotEnoughBooks, err) + }) + t.Run("book already borrowed", func(t *testing.T) { + u := entity.NewFixtureUser() + b := entity.NewFixtureBook() + u.Books = []entity.ID{b.ID} + b.Quantity = 1 + uMock.EXPECT().GetUser(u.ID).Return(u, nil) + bMock.EXPECT().GetBook(b.ID).Return(b, nil) + err := uc.Borrow(u, b) + assert.Equal(t, domain.ErrBookAlreadyBorrowed, err) + }) + t.Run("sucess", func(t *testing.T) { + u := entity.NewFixtureUser() + b := entity.NewFixtureBook() + uMock.EXPECT().GetUser(u.ID).Return(u, nil) + bMock.EXPECT().GetBook(b.ID).Return(b, nil) + uMock.EXPECT().UpdateUser(u).Return(nil) + bMock.EXPECT().UpdateBook(b).Return(nil) + err := uc.Borrow(u, b) + assert.Nil(t, err) + }) +} + +func Test_Return(t *testing.T) { + controller := gomock.NewController(t) + defer controller.Finish() + uMock := umock.NewMockUseCase(controller) + bMock := bmock.NewMockUseCase(controller) + uc := NewService(uMock, bMock) + t.Run("book not found", func(t *testing.T) { + b := entity.NewFixtureBook() + bMock.EXPECT().GetBook(b.ID).Return(nil, domain.ErrNotFound) + err := uc.Return(b) + assert.Equal(t, domain.ErrNotFound, err) + }) + t.Run("book not borrowed", func(t *testing.T) { + u := entity.NewFixtureUser() + b := entity.NewFixtureBook() + bMock.EXPECT().GetBook(b.ID).Return(b, nil) + uMock.EXPECT().ListUsers().Return([]*entity.User{u}, nil) + err := uc.Return(b) + assert.Equal(t, domain.ErrBookNotBorrowed, err) + }) + t.Run("success", func(t *testing.T) { + u := entity.NewFixtureUser() + b := entity.NewFixtureBook() + u.Books = []entity.ID{b.ID} + bMock.EXPECT().GetBook(b.ID).Return(b, nil) + uMock.EXPECT().GetUser(u.ID).Return(u, nil) + uMock.EXPECT().ListUsers().Return([]*entity.User{u}, nil) + uMock.EXPECT().UpdateUser(u).Return(nil) + bMock.EXPECT().UpdateBook(b).Return(nil) + err := uc.Return(b) + assert.Nil(t, err) + }) +} diff --git a/domain/usecase/loan/usecase_test.go b/domain/usecase/loan/usecase_test.go deleted file mode 100644 index 233b02d..0000000 --- a/domain/usecase/loan/usecase_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package loan - -import ( - "testing" - - "github.com/eminetto/clean-architecture-go-v2/domain/entity" - - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/book" - bmock "github.com/eminetto/clean-architecture-go-v2/domain/entity/book/mock" - "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" - umock "github.com/eminetto/clean-architecture-go-v2/domain/entity/user/mock" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" -) - -func Test_Borrow(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - uMock := umock.NewMockManager(controller) - bMock := bmock.NewMockManager(controller) - uc := NewUseCase(uMock, bMock) - t.Run("user not found", func(t *testing.T) { - u := user.NewFixtureUser() - b := book.NewFixtureBook() - uMock.EXPECT().Get(u.ID).Return(nil, domain.ErrNotFound) - err := uc.Borrow(u, b) - assert.Equal(t, domain.ErrNotFound, err) - }) - t.Run("book not found", func(t *testing.T) { - u := user.NewFixtureUser() - b := book.NewFixtureBook() - uMock.EXPECT().Get(u.ID).Return(u, nil) - bMock.EXPECT().Get(b.ID).Return(nil, domain.ErrNotFound) - err := uc.Borrow(u, b) - assert.Equal(t, domain.ErrNotFound, err) - }) - t.Run("not enough books to borrow", func(t *testing.T) { - u := user.NewFixtureUser() - b := book.NewFixtureBook() - b.Quantity = 0 - uMock.EXPECT().Get(u.ID).Return(u, nil) - bMock.EXPECT().Get(b.ID).Return(b, nil) - err := uc.Borrow(u, b) - assert.Equal(t, domain.ErrNotEnoughBooks, err) - }) - t.Run("book already borrowed", func(t *testing.T) { - u := user.NewFixtureUser() - b := book.NewFixtureBook() - u.Books = []entity.ID{b.ID} - b.Quantity = 1 - uMock.EXPECT().Get(u.ID).Return(u, nil) - bMock.EXPECT().Get(b.ID).Return(b, nil) - err := uc.Borrow(u, b) - assert.Equal(t, domain.ErrBookAlreadyBorrowed, err) - }) - t.Run("sucess", func(t *testing.T) { - u := user.NewFixtureUser() - b := book.NewFixtureBook() - uMock.EXPECT().Get(u.ID).Return(u, nil) - bMock.EXPECT().Get(b.ID).Return(b, nil) - uMock.EXPECT().Update(u).Return(nil) - bMock.EXPECT().Update(b).Return(nil) - err := uc.Borrow(u, b) - assert.Nil(t, err) - }) -} - -func Test_Return(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - uMock := umock.NewMockManager(controller) - bMock := bmock.NewMockManager(controller) - uc := NewUseCase(uMock, bMock) - t.Run("book not found", func(t *testing.T) { - b := book.NewFixtureBook() - bMock.EXPECT().Get(b.ID).Return(nil, domain.ErrNotFound) - err := uc.Return(b) - assert.Equal(t, domain.ErrNotFound, err) - }) - t.Run("book not borrowed", func(t *testing.T) { - u := user.NewFixtureUser() - b := book.NewFixtureBook() - bMock.EXPECT().Get(b.ID).Return(b, nil) - uMock.EXPECT().List().Return([]*user.User{u}, nil) - err := uc.Return(b) - assert.Equal(t, domain.ErrBookNotBorrowed, err) - }) - t.Run("success", func(t *testing.T) { - u := user.NewFixtureUser() - b := book.NewFixtureBook() - u.Books = []entity.ID{b.ID} - bMock.EXPECT().Get(b.ID).Return(b, nil) - uMock.EXPECT().Get(u.ID).Return(u, nil) - uMock.EXPECT().List().Return([]*user.User{u}, nil) - uMock.EXPECT().Update(u).Return(nil) - bMock.EXPECT().Update(b).Return(nil) - err := uc.Return(b) - assert.Nil(t, err) - }) -} diff --git a/domain/usecase/user/interface.go b/domain/usecase/user/interface.go new file mode 100644 index 0000000..7a81232 --- /dev/null +++ b/domain/usecase/user/interface.go @@ -0,0 +1,15 @@ +package user + +import ( + "github.com/eminetto/clean-architecture-go-v2/domain/entity" +) + +//UseCase interface +type UseCase interface { + GetUser(id entity.ID) (*entity.User, error) + SearchUsers(query string) ([]*entity.User, error) + ListUsers() ([]*entity.User, error) + CreateUser(e *entity.User) (entity.ID, error) + UpdateUser(e *entity.User) error + DeleteUser(id entity.ID) error +} diff --git a/domain/usecase/user/mock/user.go b/domain/usecase/user/mock/user.go new file mode 100644 index 0000000..9b0b802 --- /dev/null +++ b/domain/usecase/user/mock/user.go @@ -0,0 +1,123 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: domain/usecase/user/interface.go + +// Package mock is a generated GoMock package. +package mock + +import ( + reflect "reflect" + + entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" + gomock "github.com/golang/mock/gomock" +) + +// MockUseCase is a mock of UseCase interface +type MockUseCase struct { + ctrl *gomock.Controller + recorder *MockUseCaseMockRecorder +} + +// MockUseCaseMockRecorder is the mock recorder for MockUseCase +type MockUseCaseMockRecorder struct { + mock *MockUseCase +} + +// NewMockUseCase creates a new mock instance +func NewMockUseCase(ctrl *gomock.Controller) *MockUseCase { + mock := &MockUseCase{ctrl: ctrl} + mock.recorder = &MockUseCaseMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockUseCase) EXPECT() *MockUseCaseMockRecorder { + return m.recorder +} + +// GetUser mocks base method +func (m *MockUseCase) GetUser(id entity.ID) (*entity.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUser", id) + ret0, _ := ret[0].(*entity.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetUser indicates an expected call of GetUser +func (mr *MockUseCaseMockRecorder) GetUser(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUser", reflect.TypeOf((*MockUseCase)(nil).GetUser), id) +} + +// SearchUsers mocks base method +func (m *MockUseCase) SearchUsers(query string) ([]*entity.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SearchUsers", query) + ret0, _ := ret[0].([]*entity.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SearchUsers indicates an expected call of SearchUsers +func (mr *MockUseCaseMockRecorder) SearchUsers(query interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchUsers", reflect.TypeOf((*MockUseCase)(nil).SearchUsers), query) +} + +// ListUsers mocks base method +func (m *MockUseCase) ListUsers() ([]*entity.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListUsers") + ret0, _ := ret[0].([]*entity.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListUsers indicates an expected call of ListUsers +func (mr *MockUseCaseMockRecorder) ListUsers() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUsers", reflect.TypeOf((*MockUseCase)(nil).ListUsers)) +} + +// CreateUser mocks base method +func (m *MockUseCase) CreateUser(e *entity.User) (entity.ID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateUser", e) + ret0, _ := ret[0].(entity.ID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateUser indicates an expected call of CreateUser +func (mr *MockUseCaseMockRecorder) CreateUser(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockUseCase)(nil).CreateUser), e) +} + +// UpdateUser mocks base method +func (m *MockUseCase) UpdateUser(e *entity.User) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateUser", e) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateUser indicates an expected call of UpdateUser +func (mr *MockUseCaseMockRecorder) UpdateUser(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUser", reflect.TypeOf((*MockUseCase)(nil).UpdateUser), e) +} + +// DeleteUser mocks base method +func (m *MockUseCase) DeleteUser(id entity.ID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteUser", id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteUser indicates an expected call of DeleteUser +func (mr *MockUseCaseMockRecorder) DeleteUser(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUser", reflect.TypeOf((*MockUseCase)(nil).DeleteUser), id) +} diff --git a/domain/entity/user/manager.go b/domain/usecase/user/service.go similarity index 54% rename from domain/entity/user/manager.go rename to domain/usecase/user/service.go index 117c16e..d270db4 100644 --- a/domain/entity/user/manager.go +++ b/domain/usecase/user/service.go @@ -4,6 +4,8 @@ import ( "strings" "time" + repo "github.com/eminetto/clean-architecture-go-v2/domain/repository/user" + "github.com/eminetto/clean-architecture-go-v2/domain" "github.com/eminetto/clean-architecture-go-v2/pkg/password" @@ -11,22 +13,22 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) -//manager interface -type manager struct { - repo repository +//Service interface +type Service struct { + repo repo.Repository pwd password.Service } -//NewManager create new repository -func NewManager(r repository, pwd password.Service) *manager { - return &manager{ +//NewService create new use case +func NewService(r repo.Repository, pwd password.Service) *Service { + return &Service{ repo: r, pwd: pwd, } } //Create an user -func (s *manager) Create(e *User) (entity.ID, error) { +func (s *Service) CreateUser(e *entity.User) (entity.ID, error) { e.ID = entity.NewID() e.CreatedAt = time.Now() pwd, err := s.pwd.Generate(e.Password) @@ -38,23 +40,26 @@ func (s *manager) Create(e *User) (entity.ID, error) { } //Get an user -func (s *manager) Get(id entity.ID) (*User, error) { +func (s *Service) GetUser(id entity.ID) (*entity.User, error) { return s.repo.Get(id) } //Search users -func (s *manager) Search(query string) ([]*User, error) { +func (s *Service) SearchUsers(query string) ([]*entity.User, error) { return s.repo.Search(strings.ToLower(query)) } //List users -func (s *manager) List() ([]*User, error) { +func (s *Service) ListUsers() ([]*entity.User, error) { return s.repo.List() } //Delete an user -func (s *manager) Delete(id entity.ID) error { - u, err := s.Get(id) +func (s *Service) DeleteUser(id entity.ID) error { + u, err := s.GetUser(id) + if u == nil { + return domain.ErrNotFound + } if err != nil { return err } @@ -65,7 +70,7 @@ func (s *manager) Delete(id entity.ID) error { } //Update an user -func (s *manager) Update(e *User) error { +func (s *Service) UpdateUser(e *entity.User) error { e.UpdatedAt = time.Now() return s.repo.Update(e) } diff --git a/domain/entity/user/manager_test.go b/domain/usecase/user/service_test.go similarity index 55% rename from domain/entity/user/manager_test.go rename to domain/usecase/user/service_test.go index dba9b6e..f44042a 100644 --- a/domain/entity/user/manager_test.go +++ b/domain/usecase/user/service_test.go @@ -3,6 +3,8 @@ package user import ( "testing" + "github.com/eminetto/clean-architecture-go-v2/domain/repository/user" + "github.com/eminetto/clean-architecture-go-v2/pkg/password" "github.com/eminetto/clean-architecture-go-v2/domain" @@ -12,10 +14,10 @@ import ( ) func Test_Create(t *testing.T) { - repo := NewInmemRepository() - m := NewManager(repo, password.NewFakeService()) - u := NewFixtureUser() - id, err := m.Create(u) + repo := user.NewInmemRepository() + m := NewService(repo, password.NewFakeService()) + u := entity.NewFixtureUser() + id, err := m.CreateUser(u) assert.Nil(t, err) assert.Equal(t, u.ID, id) assert.False(t, u.CreatedAt.IsZero()) @@ -23,49 +25,49 @@ func Test_Create(t *testing.T) { } func Test_SearchAndFind(t *testing.T) { - repo := NewInmemRepository() - m := NewManager(repo, password.NewFakeService()) - u1 := NewFixtureUser() - u2 := NewFixtureUser() + repo := user.NewInmemRepository() + m := NewService(repo, password.NewFakeService()) + u1 := entity.NewFixtureUser() + u2 := entity.NewFixtureUser() u2.FirstName = "Lemmy" - uID, _ := m.Create(u1) - _, _ = m.Create(u2) + uID, _ := m.CreateUser(u1) + _, _ = m.CreateUser(u2) t.Run("search", func(t *testing.T) { - c, err := m.Search("ozzy") + c, err := m.SearchUsers("ozzy") assert.Nil(t, err) assert.Equal(t, 1, len(c)) assert.Equal(t, "Osbourne", c[0].LastName) - c, err = m.Search("dio") + c, err = m.SearchUsers("dio") assert.Equal(t, domain.ErrNotFound, err) assert.Nil(t, c) }) t.Run("list all", func(t *testing.T) { - all, err := m.List() + all, err := m.ListUsers() assert.Nil(t, err) assert.Equal(t, 2, len(all)) }) t.Run("get", func(t *testing.T) { - saved, err := m.Get(uID) + saved, err := m.GetUser(uID) assert.Nil(t, err) assert.Equal(t, u1.FirstName, saved.FirstName) }) } func Test_Update(t *testing.T) { - repo := NewInmemRepository() - m := NewManager(repo, password.NewFakeService()) - u := NewFixtureUser() - id, err := m.Create(u) + repo := user.NewInmemRepository() + m := NewService(repo, password.NewFakeService()) + u := entity.NewFixtureUser() + id, err := m.CreateUser(u) assert.Nil(t, err) - saved, _ := m.Get(id) + saved, _ := m.GetUser(id) saved.FirstName = "Dio" saved.Books = append(saved.Books, entity.NewID()) - assert.Nil(t, m.Update(saved)) - updated, err := m.Get(id) + assert.Nil(t, m.UpdateUser(saved)) + updated, err := m.GetUser(id) assert.Nil(t, err) assert.Equal(t, "Dio", updated.FirstName) assert.False(t, u.UpdatedAt.IsZero()) @@ -73,23 +75,23 @@ func Test_Update(t *testing.T) { } func TestDelete(t *testing.T) { - repo := NewInmemRepository() - m := NewManager(repo, password.NewFakeService()) - u1 := NewFixtureUser() - u2 := NewFixtureUser() - u2ID, _ := m.Create(u2) + repo := user.NewInmemRepository() + m := NewService(repo, password.NewFakeService()) + u1 := entity.NewFixtureUser() + u2 := entity.NewFixtureUser() + u2ID, _ := m.CreateUser(u2) - err := m.Delete(u1.ID) + err := m.DeleteUser(u1.ID) assert.Equal(t, domain.ErrNotFound, err) - err = m.Delete(u2ID) + err = m.DeleteUser(u2ID) assert.Nil(t, err) - _, err = m.Get(u2ID) + _, err = m.GetUser(u2ID) assert.Equal(t, domain.ErrNotFound, err) - u3 := NewFixtureUser() + u3 := entity.NewFixtureUser() u3.Books = []entity.ID{entity.NewID()} - _, _ = m.Create(u3) - err = m.Delete(u3.ID) + _, _ = m.CreateUser(u3) + err = m.DeleteUser(u3.ID) assert.Equal(t, domain.ErrCannotBeDeleted, err) } diff --git a/go.mod b/go.mod index 36aa8a5..0faee33 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/codegangsta/negroni v0.3.0 github.com/eminetto/clean-architecture-go v0.0.0-20200312132632-3195811c74a3 github.com/go-sql-driver/mysql v1.5.0 - github.com/golang/mock v1.4.3 + github.com/golang/mock v1.4.4 github.com/google/uuid v1.1.1 github.com/gorilla/context v1.1.1 github.com/gorilla/mux v1.6.2 @@ -17,4 +17,5 @@ require ( github.com/stretchr/testify v1.6.1 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce + rsc.io/quote/v3 v3.1.0 // indirect ) diff --git a/go.sum b/go.sum index 23e5870..ffbed44 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= From 2108f05d556ef13791d8f255ca63ad9452c9d4d4 Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Tue, 29 Sep 2020 21:53:33 -0300 Subject: [PATCH 02/12] fix: remove file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e660fd9..6df9c8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ bin/ +.idea/ From d3b90139667a699ac15bc5d4dabc644d44e0d3bd Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Tue, 29 Sep 2020 21:54:02 -0300 Subject: [PATCH 03/12] feat: improvements in structures --- .idea/codeStyles/codeStyleConfig.xml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .idea/codeStyles/codeStyleConfig.xml diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a1..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file From 6711a4055f4a700cf07228e51a88936460d5128b Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Tue, 29 Sep 2020 22:07:32 -0300 Subject: [PATCH 04/12] feat: improvements in structures --- api/handler/{ => book}/book.go | 6 +++--- api/handler/{ => book}/book_test.go | 10 +++++----- api/handler/{ => loan}/loan.go | 6 +++--- api/handler/{ => loan}/loan_test.go | 6 +++--- api/handler/{ => user}/user.go | 6 +++--- api/handler/{ => user}/user_test.go | 10 +++++----- api/main.go | 16 +++++++++------- 7 files changed, 31 insertions(+), 29 deletions(-) rename api/handler/{ => book}/book.go (97%) rename api/handler/{ => book}/book_test.go (96%) rename api/handler/{ => loan}/loan.go (94%) rename api/handler/{ => loan}/loan_test.go (97%) rename api/handler/{ => user}/user.go (97%) rename api/handler/{ => user}/user_test.go (96%) diff --git a/api/handler/book.go b/api/handler/book/book.go similarity index 97% rename from api/handler/book.go rename to api/handler/book/book.go index 7cad6f5..cdf181a 100644 --- a/api/handler/book.go +++ b/api/handler/book/book.go @@ -1,4 +1,4 @@ -package handler +package book import ( "encoding/json" @@ -162,8 +162,8 @@ func deleteBook(service book.UseCase) http.Handler { }) } -//MakeBookHandlers make url handlers -func MakeBookHandlers(r *mux.Router, n negroni.Negroni, service book.UseCase) { +//MakeHandlers make url handlers +func MakeHandlers(r *mux.Router, n negroni.Negroni, service book.UseCase) { r.Handle("/v1/book", n.With( negroni.Wrap(listBooks(service)), )).Methods("GET", "OPTIONS").Name("listBooks") diff --git a/api/handler/book_test.go b/api/handler/book/book_test.go similarity index 96% rename from api/handler/book_test.go rename to api/handler/book/book_test.go index 7c51eab..5be49f7 100644 --- a/api/handler/book_test.go +++ b/api/handler/book/book_test.go @@ -1,4 +1,4 @@ -package handler +package book import ( "encoding/json" @@ -24,7 +24,7 @@ func Test_listBooks(t *testing.T) { manager := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeBookHandlers(r, *n, manager) + MakeHandlers(r, *n, manager) path, err := r.GetRoute("listBooks").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book", path) @@ -74,7 +74,7 @@ func Test_createBook(t *testing.T) { manager := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeBookHandlers(r, *n, manager) + MakeHandlers(r, *n, manager) path, err := r.GetRoute("createBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book", path) @@ -106,7 +106,7 @@ func Test_getBook(t *testing.T) { manager := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeBookHandlers(r, *n, manager) + MakeHandlers(r, *n, manager) path, err := r.GetRoute("getBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book/{id}", path) @@ -133,7 +133,7 @@ func Test_deleteBook(t *testing.T) { manager := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeBookHandlers(r, *n, manager) + MakeHandlers(r, *n, manager) path, err := r.GetRoute("deleteBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book/{id}", path) diff --git a/api/handler/loan.go b/api/handler/loan/loan.go similarity index 94% rename from api/handler/loan.go rename to api/handler/loan/loan.go index 8e655a3..a621b9c 100644 --- a/api/handler/loan.go +++ b/api/handler/loan/loan.go @@ -1,4 +1,4 @@ -package handler +package loan import ( "fmt" @@ -96,8 +96,8 @@ func returnBook(bookService book.UseCase, loanService loan.UseCase) http.Handler }) } -//MakeLoanHandlers make url handlers -func MakeLoanHandlers(r *mux.Router, n negroni.Negroni, bookService book.UseCase, userService user.UseCase, loanService loan.UseCase) { +//MakeHandlers make url handlers +func MakeHandlers(r *mux.Router, n negroni.Negroni, bookService book.UseCase, userService user.UseCase, loanService loan.UseCase) { r.Handle("/v1/loan/borrow/{book_id}/{user_id}", n.With( negroni.Wrap(borrowBook(bookService, userService, loanService)), )).Methods("GET", "OPTIONS").Name("borrowBook") diff --git a/api/handler/loan_test.go b/api/handler/loan/loan_test.go similarity index 97% rename from api/handler/loan_test.go rename to api/handler/loan/loan_test.go index 82b838a..b1d7408 100644 --- a/api/handler/loan_test.go +++ b/api/handler/loan/loan_test.go @@ -1,4 +1,4 @@ -package handler +package loan import ( "fmt" @@ -26,7 +26,7 @@ func Test_borrowBook(t *testing.T) { lMock := lmock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeLoanHandlers(r, *n, bMock, uMock, lMock) + MakeHandlers(r, *n, bMock, uMock, lMock) path, err := r.GetRoute("borrowBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/loan/borrow/{book_id}/{user_id}", path) @@ -75,7 +75,7 @@ func Test_returnBook(t *testing.T) { lMock := lmock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeLoanHandlers(r, *n, bMock, uMock, lMock) + MakeHandlers(r, *n, bMock, uMock, lMock) path, err := r.GetRoute("returnBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/loan/return/{book_id}", path) diff --git a/api/handler/user.go b/api/handler/user/user.go similarity index 97% rename from api/handler/user.go rename to api/handler/user/user.go index f0945c8..1472e94 100644 --- a/api/handler/user.go +++ b/api/handler/user/user.go @@ -1,4 +1,4 @@ -package handler +package user import ( "encoding/json" @@ -162,8 +162,8 @@ func deleteUser(service user.UseCase) http.Handler { }) } -//MakeUserHandlers make url handlers -func MakeUserHandlers(r *mux.Router, n negroni.Negroni, service user.UseCase) { +//MakeHandlers make url handlers +func MakeHandlers(r *mux.Router, n negroni.Negroni, service user.UseCase) { r.Handle("/v1/user", n.With( negroni.Wrap(listUsers(service)), )).Methods("GET", "OPTIONS").Name("listUsers") diff --git a/api/handler/user_test.go b/api/handler/user/user_test.go similarity index 96% rename from api/handler/user_test.go rename to api/handler/user/user_test.go index d3d045f..9f60833 100644 --- a/api/handler/user_test.go +++ b/api/handler/user/user_test.go @@ -1,4 +1,4 @@ -package handler +package user import ( "encoding/json" @@ -24,7 +24,7 @@ func Test_listUsers(t *testing.T) { m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeUserHandlers(r, *n, m) + MakeHandlers(r, *n, m) path, err := r.GetRoute("listUsers").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user", path) @@ -74,7 +74,7 @@ func Test_createUser(t *testing.T) { m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeUserHandlers(r, *n, m) + MakeHandlers(r, *n, m) path, err := r.GetRoute("createUser").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user", path) @@ -107,7 +107,7 @@ func Test_getUser(t *testing.T) { m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeUserHandlers(r, *n, m) + MakeHandlers(r, *n, m) path, err := r.GetRoute("getUser").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user/{id}", path) @@ -134,7 +134,7 @@ func Test_deleteUser(t *testing.T) { m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeUserHandlers(r, *n, m) + MakeHandlers(r, *n, m) path, err := r.GetRoute("deleteUser").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user/{id}", path) diff --git a/api/main.go b/api/main.go index b659bd0..47f80df 100644 --- a/api/main.go +++ b/api/main.go @@ -21,7 +21,9 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/codegangsta/negroni" - "github.com/eminetto/clean-architecture-go-v2/api/handler" + bhandler "github.com/eminetto/clean-architecture-go-v2/api/handler/book" + lhandler "github.com/eminetto/clean-architecture-go-v2/api/handler/loan" + uhandler "github.com/eminetto/clean-architecture-go-v2/api/handler/user" "github.com/eminetto/clean-architecture-go-v2/api/middleware" "github.com/eminetto/clean-architecture-go-v2/config" "github.com/eminetto/clean-architecture-go-v2/pkg/metric" @@ -40,12 +42,12 @@ func main() { defer db.Close() bookRepo := brepo.NewMySQLRepository(db) - bookManager := book.NewService(bookRepo) + bookService := book.NewService(bookRepo) userRepo := urepo.NewMySQLRepoRepository(db) - userManager := user.NewService(userRepo, password.NewService()) + userService := user.NewService(userRepo, password.NewService()) - loanUseCase := loan.NewService(userManager, bookManager) + loanUseCase := loan.NewService(userService, bookService) metricService, err := metric.NewPrometheusService() if err != nil { @@ -59,13 +61,13 @@ func main() { negroni.NewLogger(), ) //book - handler.MakeBookHandlers(r, *n, bookManager) + bhandler.MakeHandlers(r, *n, bookService) //user - handler.MakeUserHandlers(r, *n, userManager) + uhandler.MakeHandlers(r, *n, userService) //loan - handler.MakeLoanHandlers(r, *n, bookManager, userManager, loanUseCase) + lhandler.MakeHandlers(r, *n, bookService, userService, loanUseCase) http.Handle("/", r) http.Handle("/metrics", promhttp.Handler()) From c008dbab290477fbeed96d2bdfbb27e76ef8cc79 Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Thu, 1 Oct 2020 08:30:36 -0300 Subject: [PATCH 05/12] feat: improvements in structures --- api/main.go | 4 +- cmd/main.go | 2 +- domain/repository/book/interface.go | 25 -- domain/repository/user/interface.go | 25 -- domain/usecase/book/interface.go | 20 ++ domain/usecase/book/mock/book.go | 248 +++++++++++++++++- domain/usecase/book/service.go | 18 +- domain/usecase/book/service_test.go | 2 +- domain/usecase/loan/mock/loan.go | 3 +- domain/usecase/user/interface.go | 20 ++ domain/usecase/user/mock/user.go | 248 +++++++++++++++++- domain/usecase/user/service.go | 18 +- domain/usecase/user/service_test.go | 2 +- infra/{ => db}/init.sql | 0 .../repository/book/repository_inmem.go | 0 .../repository/book/repository_mysql.go | 0 .../repository/user/repository_inmem.go | 0 .../repository/user/repository_mysql.go | 0 18 files changed, 554 insertions(+), 81 deletions(-) delete mode 100644 domain/repository/book/interface.go delete mode 100644 domain/repository/user/interface.go rename infra/{ => db}/init.sql (100%) rename {domain => infra}/repository/book/repository_inmem.go (100%) rename {domain => infra}/repository/book/repository_mysql.go (100%) rename {domain => infra}/repository/user/repository_inmem.go (100%) rename {domain => infra}/repository/user/repository_mysql.go (100%) diff --git a/api/main.go b/api/main.go index 47f80df..cb11ac2 100644 --- a/api/main.go +++ b/api/main.go @@ -9,10 +9,10 @@ import ( "strconv" "time" - brepo "github.com/eminetto/clean-architecture-go-v2/domain/repository/book" - urepo "github.com/eminetto/clean-architecture-go-v2/domain/repository/user" "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user" + brepo "github.com/eminetto/clean-architecture-go-v2/infra/repository/book" + urepo "github.com/eminetto/clean-architecture-go-v2/infra/repository/user" "github.com/eminetto/clean-architecture-go-v2/pkg/password" diff --git a/cmd/main.go b/cmd/main.go index 4964e46..cddf8c5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,8 +7,8 @@ import ( "log" "os" - repo "github.com/eminetto/clean-architecture-go-v2/domain/repository/book" book "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" + repo "github.com/eminetto/clean-architecture-go-v2/infra/repository/book" "github.com/eminetto/clean-architecture-go-v2/config" _ "github.com/go-sql-driver/mysql" diff --git a/domain/repository/book/interface.go b/domain/repository/book/interface.go deleted file mode 100644 index 8a1fa36..0000000 --- a/domain/repository/book/interface.go +++ /dev/null @@ -1,25 +0,0 @@ -package book - -import ( - "github.com/eminetto/clean-architecture-go-v2/domain/entity" -) - -//Reader interface -type Reader interface { - Get(id entity.ID) (*entity.Book, error) - Search(query string) ([]*entity.Book, error) - List() ([]*entity.Book, error) -} - -//Writer book writer -type Writer interface { - Create(e *entity.Book) (entity.ID, error) - Update(e *entity.Book) error - Delete(id entity.ID) error -} - -//repository interface -type Repository interface { - Reader - Writer -} diff --git a/domain/repository/user/interface.go b/domain/repository/user/interface.go deleted file mode 100644 index 8180476..0000000 --- a/domain/repository/user/interface.go +++ /dev/null @@ -1,25 +0,0 @@ -package user - -import ( - "github.com/eminetto/clean-architecture-go-v2/domain/entity" -) - -//Reader interface -type Reader interface { - Get(id entity.ID) (*entity.User, error) - Search(query string) ([]*entity.User, error) - List() ([]*entity.User, error) -} - -//Writer user writer -type Writer interface { - Create(e *entity.User) (entity.ID, error) - Update(e *entity.User) error - Delete(id entity.ID) error -} - -//Repository interface -type Repository interface { - Reader - Writer -} diff --git a/domain/usecase/book/interface.go b/domain/usecase/book/interface.go index a9bf4a6..0382f8a 100644 --- a/domain/usecase/book/interface.go +++ b/domain/usecase/book/interface.go @@ -4,6 +4,26 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) +//Reader interface +type Reader interface { + Get(id entity.ID) (*entity.Book, error) + Search(query string) ([]*entity.Book, error) + List() ([]*entity.Book, error) +} + +//Writer book writer +type Writer interface { + Create(e *entity.Book) (entity.ID, error) + Update(e *entity.Book) error + Delete(id entity.ID) error +} + +//Repository interface +type Repository interface { + Reader + Writer +} + //UseCase interface type UseCase interface { GetBook(id entity.ID) (*entity.Book, error) diff --git a/domain/usecase/book/mock/book.go b/domain/usecase/book/mock/book.go index 9686c0f..932c503 100644 --- a/domain/usecase/book/mock/book.go +++ b/domain/usecase/book/mock/book.go @@ -5,12 +5,256 @@ package mock import ( - reflect "reflect" - entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" gomock "github.com/golang/mock/gomock" + reflect "reflect" ) +// MockReader is a mock of Reader interface +type MockReader struct { + ctrl *gomock.Controller + recorder *MockReaderMockRecorder +} + +// MockReaderMockRecorder is the mock recorder for MockReader +type MockReaderMockRecorder struct { + mock *MockReader +} + +// NewMockReader creates a new mock instance +func NewMockReader(ctrl *gomock.Controller) *MockReader { + mock := &MockReader{ctrl: ctrl} + mock.recorder = &MockReaderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockReader) EXPECT() *MockReaderMockRecorder { + return m.recorder +} + +// Get mocks base method +func (m *MockReader) Get(id entity.ID) (*entity.Book, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", id) + ret0, _ := ret[0].(*entity.Book) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get +func (mr *MockReaderMockRecorder) Get(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockReader)(nil).Get), id) +} + +// Search mocks base method +func (m *MockReader) Search(query string) ([]*entity.Book, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Search", query) + ret0, _ := ret[0].([]*entity.Book) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Search indicates an expected call of Search +func (mr *MockReaderMockRecorder) Search(query interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockReader)(nil).Search), query) +} + +// List mocks base method +func (m *MockReader) List() ([]*entity.Book, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List") + ret0, _ := ret[0].([]*entity.Book) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List +func (mr *MockReaderMockRecorder) List() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockReader)(nil).List)) +} + +// MockWriter is a mock of Writer interface +type MockWriter struct { + ctrl *gomock.Controller + recorder *MockWriterMockRecorder +} + +// MockWriterMockRecorder is the mock recorder for MockWriter +type MockWriterMockRecorder struct { + mock *MockWriter +} + +// NewMockWriter creates a new mock instance +func NewMockWriter(ctrl *gomock.Controller) *MockWriter { + mock := &MockWriter{ctrl: ctrl} + mock.recorder = &MockWriterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockWriter) EXPECT() *MockWriterMockRecorder { + return m.recorder +} + +// Create mocks base method +func (m *MockWriter) Create(e *entity.Book) (entity.ID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", e) + ret0, _ := ret[0].(entity.ID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create +func (mr *MockWriterMockRecorder) Create(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockWriter)(nil).Create), e) +} + +// Update mocks base method +func (m *MockWriter) Update(e *entity.Book) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", e) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update +func (mr *MockWriterMockRecorder) Update(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockWriter)(nil).Update), e) +} + +// Delete mocks base method +func (m *MockWriter) Delete(id entity.ID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", id) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete +func (mr *MockWriterMockRecorder) Delete(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockWriter)(nil).Delete), id) +} + +// MockRepository is a mock of Repository interface +type MockRepository struct { + ctrl *gomock.Controller + recorder *MockRepositoryMockRecorder +} + +// MockRepositoryMockRecorder is the mock recorder for MockRepository +type MockRepositoryMockRecorder struct { + mock *MockRepository +} + +// NewMockRepository creates a new mock instance +func NewMockRepository(ctrl *gomock.Controller) *MockRepository { + mock := &MockRepository{ctrl: ctrl} + mock.recorder = &MockRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder { + return m.recorder +} + +// Get mocks base method +func (m *MockRepository) Get(id entity.ID) (*entity.Book, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", id) + ret0, _ := ret[0].(*entity.Book) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get +func (mr *MockRepositoryMockRecorder) Get(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRepository)(nil).Get), id) +} + +// Search mocks base method +func (m *MockRepository) Search(query string) ([]*entity.Book, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Search", query) + ret0, _ := ret[0].([]*entity.Book) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Search indicates an expected call of Search +func (mr *MockRepositoryMockRecorder) Search(query interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockRepository)(nil).Search), query) +} + +// List mocks base method +func (m *MockRepository) List() ([]*entity.Book, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List") + ret0, _ := ret[0].([]*entity.Book) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List +func (mr *MockRepositoryMockRecorder) List() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRepository)(nil).List)) +} + +// Create mocks base method +func (m *MockRepository) Create(e *entity.Book) (entity.ID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", e) + ret0, _ := ret[0].(entity.ID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create +func (mr *MockRepositoryMockRecorder) Create(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockRepository)(nil).Create), e) +} + +// Update mocks base method +func (m *MockRepository) Update(e *entity.Book) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", e) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update +func (mr *MockRepositoryMockRecorder) Update(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockRepository)(nil).Update), e) +} + +// Delete mocks base method +func (m *MockRepository) Delete(id entity.ID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", id) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete +func (mr *MockRepositoryMockRecorder) Delete(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockRepository)(nil).Delete), id) +} + // MockUseCase is a mock of UseCase interface type MockUseCase struct { ctrl *gomock.Controller diff --git a/domain/usecase/book/service.go b/domain/usecase/book/service.go index adf5486..172727c 100644 --- a/domain/usecase/book/service.go +++ b/domain/usecase/book/service.go @@ -4,8 +4,6 @@ import ( "strings" "time" - repo "github.com/eminetto/clean-architecture-go-v2/domain/repository/book" - "github.com/eminetto/clean-architecture-go-v2/domain" "github.com/eminetto/clean-architecture-go-v2/domain/entity" @@ -13,24 +11,24 @@ import ( //Service book usecase type Service struct { - repo repo.Repository + repo Repository } //NewService create new service -func NewService(r repo.Repository) *Service { +func NewService(r Repository) *Service { return &Service{ repo: r, } } -//Create a book +//CreateBook create a book func (s *Service) CreateBook(e *entity.Book) (entity.ID, error) { e.ID = entity.NewID() e.CreatedAt = time.Now() return s.repo.Create(e) } -//Get a book +//GetBook get a book func (s *Service) GetBook(id entity.ID) (*entity.Book, error) { b, err := s.repo.Get(id) if b == nil { @@ -43,7 +41,7 @@ func (s *Service) GetBook(id entity.ID) (*entity.Book, error) { return b, nil } -//Search books +//SearchBooks search books func (s *Service) SearchBooks(query string) ([]*entity.Book, error) { books, err := s.repo.Search(strings.ToLower(query)) if err != nil { @@ -55,7 +53,7 @@ func (s *Service) SearchBooks(query string) ([]*entity.Book, error) { return books, nil } -//List books +//ListBooks list books func (s *Service) ListBooks() ([]*entity.Book, error) { books, err := s.repo.List() if err != nil { @@ -67,7 +65,7 @@ func (s *Service) ListBooks() ([]*entity.Book, error) { return books, nil } -//Delete a book +//DeleteBook Delete a book func (s *Service) DeleteBook(id entity.ID) error { _, err := s.GetBook(id) if err != nil { @@ -76,7 +74,7 @@ func (s *Service) DeleteBook(id entity.ID) error { return s.repo.Delete(id) } -//Update a book +//UpdateBook Update a book func (s *Service) UpdateBook(e *entity.Book) error { return s.repo.Update(e) } diff --git a/domain/usecase/book/service_test.go b/domain/usecase/book/service_test.go index 803292d..479db7f 100644 --- a/domain/usecase/book/service_test.go +++ b/domain/usecase/book/service_test.go @@ -5,7 +5,7 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" - "github.com/eminetto/clean-architecture-go-v2/domain/repository/book" + "github.com/eminetto/clean-architecture-go-v2/infra/repository/book" "github.com/eminetto/clean-architecture-go-v2/domain" diff --git a/domain/usecase/loan/mock/loan.go b/domain/usecase/loan/mock/loan.go index 5d5b355..0d25a74 100644 --- a/domain/usecase/loan/mock/loan.go +++ b/domain/usecase/loan/mock/loan.go @@ -5,10 +5,9 @@ package mock import ( - reflect "reflect" - entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" gomock "github.com/golang/mock/gomock" + reflect "reflect" ) // MockUseCase is a mock of UseCase interface diff --git a/domain/usecase/user/interface.go b/domain/usecase/user/interface.go index 7a81232..454af47 100644 --- a/domain/usecase/user/interface.go +++ b/domain/usecase/user/interface.go @@ -4,6 +4,26 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) +//Reader interface +type Reader interface { + Get(id entity.ID) (*entity.User, error) + Search(query string) ([]*entity.User, error) + List() ([]*entity.User, error) +} + +//Writer user writer +type Writer interface { + Create(e *entity.User) (entity.ID, error) + Update(e *entity.User) error + Delete(id entity.ID) error +} + +//Repository interface +type Repository interface { + Reader + Writer +} + //UseCase interface type UseCase interface { GetUser(id entity.ID) (*entity.User, error) diff --git a/domain/usecase/user/mock/user.go b/domain/usecase/user/mock/user.go index 9b0b802..a184132 100644 --- a/domain/usecase/user/mock/user.go +++ b/domain/usecase/user/mock/user.go @@ -5,12 +5,256 @@ package mock import ( - reflect "reflect" - entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" gomock "github.com/golang/mock/gomock" + reflect "reflect" ) +// MockReader is a mock of Reader interface +type MockReader struct { + ctrl *gomock.Controller + recorder *MockReaderMockRecorder +} + +// MockReaderMockRecorder is the mock recorder for MockReader +type MockReaderMockRecorder struct { + mock *MockReader +} + +// NewMockReader creates a new mock instance +func NewMockReader(ctrl *gomock.Controller) *MockReader { + mock := &MockReader{ctrl: ctrl} + mock.recorder = &MockReaderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockReader) EXPECT() *MockReaderMockRecorder { + return m.recorder +} + +// Get mocks base method +func (m *MockReader) Get(id entity.ID) (*entity.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", id) + ret0, _ := ret[0].(*entity.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get +func (mr *MockReaderMockRecorder) Get(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockReader)(nil).Get), id) +} + +// Search mocks base method +func (m *MockReader) Search(query string) ([]*entity.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Search", query) + ret0, _ := ret[0].([]*entity.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Search indicates an expected call of Search +func (mr *MockReaderMockRecorder) Search(query interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockReader)(nil).Search), query) +} + +// List mocks base method +func (m *MockReader) List() ([]*entity.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List") + ret0, _ := ret[0].([]*entity.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List +func (mr *MockReaderMockRecorder) List() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockReader)(nil).List)) +} + +// MockWriter is a mock of Writer interface +type MockWriter struct { + ctrl *gomock.Controller + recorder *MockWriterMockRecorder +} + +// MockWriterMockRecorder is the mock recorder for MockWriter +type MockWriterMockRecorder struct { + mock *MockWriter +} + +// NewMockWriter creates a new mock instance +func NewMockWriter(ctrl *gomock.Controller) *MockWriter { + mock := &MockWriter{ctrl: ctrl} + mock.recorder = &MockWriterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockWriter) EXPECT() *MockWriterMockRecorder { + return m.recorder +} + +// Create mocks base method +func (m *MockWriter) Create(e *entity.User) (entity.ID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", e) + ret0, _ := ret[0].(entity.ID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create +func (mr *MockWriterMockRecorder) Create(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockWriter)(nil).Create), e) +} + +// Update mocks base method +func (m *MockWriter) Update(e *entity.User) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", e) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update +func (mr *MockWriterMockRecorder) Update(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockWriter)(nil).Update), e) +} + +// Delete mocks base method +func (m *MockWriter) Delete(id entity.ID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", id) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete +func (mr *MockWriterMockRecorder) Delete(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockWriter)(nil).Delete), id) +} + +// MockRepository is a mock of Repository interface +type MockRepository struct { + ctrl *gomock.Controller + recorder *MockRepositoryMockRecorder +} + +// MockRepositoryMockRecorder is the mock recorder for MockRepository +type MockRepositoryMockRecorder struct { + mock *MockRepository +} + +// NewMockRepository creates a new mock instance +func NewMockRepository(ctrl *gomock.Controller) *MockRepository { + mock := &MockRepository{ctrl: ctrl} + mock.recorder = &MockRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder { + return m.recorder +} + +// Get mocks base method +func (m *MockRepository) Get(id entity.ID) (*entity.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", id) + ret0, _ := ret[0].(*entity.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get +func (mr *MockRepositoryMockRecorder) Get(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRepository)(nil).Get), id) +} + +// Search mocks base method +func (m *MockRepository) Search(query string) ([]*entity.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Search", query) + ret0, _ := ret[0].([]*entity.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Search indicates an expected call of Search +func (mr *MockRepositoryMockRecorder) Search(query interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockRepository)(nil).Search), query) +} + +// List mocks base method +func (m *MockRepository) List() ([]*entity.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List") + ret0, _ := ret[0].([]*entity.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List +func (mr *MockRepositoryMockRecorder) List() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRepository)(nil).List)) +} + +// Create mocks base method +func (m *MockRepository) Create(e *entity.User) (entity.ID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", e) + ret0, _ := ret[0].(entity.ID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create +func (mr *MockRepositoryMockRecorder) Create(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockRepository)(nil).Create), e) +} + +// Update mocks base method +func (m *MockRepository) Update(e *entity.User) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", e) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update +func (mr *MockRepositoryMockRecorder) Update(e interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockRepository)(nil).Update), e) +} + +// Delete mocks base method +func (m *MockRepository) Delete(id entity.ID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", id) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete +func (mr *MockRepositoryMockRecorder) Delete(id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockRepository)(nil).Delete), id) +} + // MockUseCase is a mock of UseCase interface type MockUseCase struct { ctrl *gomock.Controller diff --git a/domain/usecase/user/service.go b/domain/usecase/user/service.go index d270db4..5fa50e9 100644 --- a/domain/usecase/user/service.go +++ b/domain/usecase/user/service.go @@ -4,8 +4,6 @@ import ( "strings" "time" - repo "github.com/eminetto/clean-architecture-go-v2/domain/repository/user" - "github.com/eminetto/clean-architecture-go-v2/domain" "github.com/eminetto/clean-architecture-go-v2/pkg/password" @@ -15,19 +13,19 @@ import ( //Service interface type Service struct { - repo repo.Repository + repo Repository pwd password.Service } //NewService create new use case -func NewService(r repo.Repository, pwd password.Service) *Service { +func NewService(r Repository, pwd password.Service) *Service { return &Service{ repo: r, pwd: pwd, } } -//Create an user +//CreateUser Create an user func (s *Service) CreateUser(e *entity.User) (entity.ID, error) { e.ID = entity.NewID() e.CreatedAt = time.Now() @@ -39,22 +37,22 @@ func (s *Service) CreateUser(e *entity.User) (entity.ID, error) { return s.repo.Create(e) } -//Get an user +//GetUser Get an user func (s *Service) GetUser(id entity.ID) (*entity.User, error) { return s.repo.Get(id) } -//Search users +//SearchUsers Search users func (s *Service) SearchUsers(query string) ([]*entity.User, error) { return s.repo.Search(strings.ToLower(query)) } -//List users +//ListUsers List users func (s *Service) ListUsers() ([]*entity.User, error) { return s.repo.List() } -//Delete an user +//DeleteUser Delete an user func (s *Service) DeleteUser(id entity.ID) error { u, err := s.GetUser(id) if u == nil { @@ -69,7 +67,7 @@ func (s *Service) DeleteUser(id entity.ID) error { return s.repo.Delete(id) } -//Update an user +//UpdateUser Update an user func (s *Service) UpdateUser(e *entity.User) error { e.UpdatedAt = time.Now() return s.repo.Update(e) diff --git a/domain/usecase/user/service_test.go b/domain/usecase/user/service_test.go index f44042a..6a9fb8e 100644 --- a/domain/usecase/user/service_test.go +++ b/domain/usecase/user/service_test.go @@ -3,7 +3,7 @@ package user import ( "testing" - "github.com/eminetto/clean-architecture-go-v2/domain/repository/user" + "github.com/eminetto/clean-architecture-go-v2/infra/repository/user" "github.com/eminetto/clean-architecture-go-v2/pkg/password" diff --git a/infra/init.sql b/infra/db/init.sql similarity index 100% rename from infra/init.sql rename to infra/db/init.sql diff --git a/domain/repository/book/repository_inmem.go b/infra/repository/book/repository_inmem.go similarity index 100% rename from domain/repository/book/repository_inmem.go rename to infra/repository/book/repository_inmem.go diff --git a/domain/repository/book/repository_mysql.go b/infra/repository/book/repository_mysql.go similarity index 100% rename from domain/repository/book/repository_mysql.go rename to infra/repository/book/repository_mysql.go diff --git a/domain/repository/user/repository_inmem.go b/infra/repository/user/repository_inmem.go similarity index 100% rename from domain/repository/user/repository_inmem.go rename to infra/repository/user/repository_inmem.go diff --git a/domain/repository/user/repository_mysql.go b/infra/repository/user/repository_mysql.go similarity index 100% rename from domain/repository/user/repository_mysql.go rename to infra/repository/user/repository_mysql.go From 098477169a0f073d08af748f000d8847c16d2aa6 Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Mon, 5 Oct 2020 08:49:21 -0300 Subject: [PATCH 06/12] fix: move some logic to domain entities --- api/handler/book/book.go | 22 ++---- api/handler/book/book_test.go | 18 +++-- api/handler/loan/loan_test.go | 16 +++- api/handler/user/user.go | 21 ++---- api/handler/user/user_test.go | 18 +++-- api/main.go | 4 +- cmd/main.go | 4 +- domain/entity/book.go | 23 ++++++ domain/entity/book_fixture.go | 16 ---- domain/entity/book_test.go | 71 ++++++++++++++++++ domain/entity/user.go | 67 +++++++++++++++++ domain/entity/user_fixture.go | 16 ---- domain/entity/user_test.go | 109 ++++++++++++++++++++++++++++ domain/error.go | 3 + domain/usecase/book/interface.go | 2 +- domain/usecase/book/mock/book.go | 11 +-- domain/usecase/book/service.go | 19 ++++- domain/usecase/book/service_test.go | 34 ++++++--- domain/usecase/loan/mock/loan.go | 3 +- domain/usecase/loan/service.go | 41 ++++++----- domain/usecase/loan/service_test.go | 65 ++++++++++++----- domain/usecase/user/interface.go | 2 +- domain/usecase/user/mock/user.go | 11 +-- domain/usecase/user/service.go | 23 +++--- domain/usecase/user/service_test.go | 57 +++++++++------ 25 files changed, 493 insertions(+), 183 deletions(-) delete mode 100644 domain/entity/book_fixture.go create mode 100644 domain/entity/book_test.go delete mode 100644 domain/entity/user_fixture.go create mode 100644 domain/entity/user_test.go diff --git a/api/handler/book/book.go b/api/handler/book/book.go index cdf181a..27577fe 100644 --- a/api/handler/book/book.go +++ b/api/handler/book/book.go @@ -4,7 +4,6 @@ import ( "encoding/json" "log" "net/http" - "time" "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" @@ -74,27 +73,18 @@ func createBook(service book.UseCase) http.Handler { w.Write([]byte(errorMessage)) return } - b := &entity.Book{ - ID: entity.NewID(), - Title: input.Title, - Author: input.Author, - Pages: input.Pages, - Quantity: input.Quantity, - CreatedAt: time.Now(), - } - b.ID, err = service.CreateBook(b) + id, err := service.CreateBook(input.Title, input.Author, input.Pages, input.Quantity) if err != nil { - log.Println(err.Error()) w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return } toJ := &presenter.Book{ - ID: b.ID, - Title: b.Title, - Author: b.Author, - Pages: b.Pages, - Quantity: b.Quantity, + ID: id, + Title: input.Title, + Author: input.Author, + Pages: input.Pages, + Quantity: input.Quantity, } w.WriteHeader(http.StatusCreated) diff --git a/api/handler/book/book_test.go b/api/handler/book/book_test.go index 5be49f7..67cd612 100644 --- a/api/handler/book/book_test.go +++ b/api/handler/book/book_test.go @@ -28,7 +28,9 @@ func Test_listBooks(t *testing.T) { path, err := r.GetRoute("listBooks").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book", path) - b := entity.NewFixtureBook() + b := &entity.Book{ + ID: entity.NewID(), + } manager.EXPECT(). ListBooks(). Return([]*entity.Book{b}, nil) @@ -57,7 +59,9 @@ func Test_listBooks_Search(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() manager := mock.NewMockUseCase(controller) - b := entity.NewFixtureBook() + b := &entity.Book{ + ID: entity.NewID(), + } manager.EXPECT(). SearchBooks("ozzy"). Return([]*entity.Book{b}, nil) @@ -80,7 +84,7 @@ func Test_createBook(t *testing.T) { assert.Equal(t, "/v1/book", path) manager.EXPECT(). - CreateBook(gomock.Any()). + CreateBook(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(entity.NewID(), nil) h := createBook(manager) @@ -110,7 +114,9 @@ func Test_getBook(t *testing.T) { path, err := r.GetRoute("getBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book/{id}", path) - b := entity.NewFixtureBook() + b := &entity.Book{ + ID: entity.NewID(), + } manager.EXPECT(). GetBook(b.ID). Return(b, nil) @@ -137,7 +143,9 @@ func Test_deleteBook(t *testing.T) { path, err := r.GetRoute("deleteBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book/{id}", path) - b := entity.NewFixtureBook() + b := &entity.Book{ + ID: entity.NewID(), + } manager.EXPECT().DeleteBook(b.ID).Return(nil) handler := deleteBook(manager) req, _ := http.NewRequest("DELETE", "/v1/bookmark/"+b.ID.String(), nil) diff --git a/api/handler/loan/loan_test.go b/api/handler/loan/loan_test.go index b1d7408..881d2b9 100644 --- a/api/handler/loan/loan_test.go +++ b/api/handler/loan/loan_test.go @@ -43,7 +43,9 @@ func Test_borrowBook(t *testing.T) { assert.Equal(t, http.StatusNotFound, res.StatusCode) }) t.Run("user not found", func(t *testing.T) { - b := entity.NewFixtureBook() + b := &entity.Book{ + ID: entity.NewID(), + } uID := entity.NewID() bMock.EXPECT().GetBook(b.ID).Return(b, nil) uMock.EXPECT().GetUser(uID).Return(nil, domain.ErrNotFound) @@ -54,8 +56,12 @@ func Test_borrowBook(t *testing.T) { assert.Equal(t, http.StatusNotFound, res.StatusCode) }) t.Run("success", func(t *testing.T) { - b := entity.NewFixtureBook() - u := entity.NewFixtureUser() + b := &entity.Book{ + ID: entity.NewID(), + } + u := &entity.User{ + ID: entity.NewID(), + } bMock.EXPECT().GetBook(b.ID).Return(b, nil) uMock.EXPECT().GetUser(u.ID).Return(u, nil) lMock.EXPECT().Borrow(u, b).Return(nil) @@ -91,7 +97,9 @@ func Test_returnBook(t *testing.T) { assert.Equal(t, http.StatusNotFound, res.StatusCode) }) t.Run("success", func(t *testing.T) { - b := entity.NewFixtureBook() + b := &entity.Book{ + ID: entity.NewID(), + } bMock.EXPECT().GetBook(b.ID).Return(b, nil) lMock.EXPECT().Return(b).Return(nil) ts := httptest.NewServer(r) diff --git a/api/handler/user/user.go b/api/handler/user/user.go index 1472e94..1fb5d2c 100644 --- a/api/handler/user/user.go +++ b/api/handler/user/user.go @@ -4,7 +4,6 @@ import ( "encoding/json" "log" "net/http" - "time" "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user" @@ -74,16 +73,7 @@ func createUser(service user.UseCase) http.Handler { w.Write([]byte(errorMessage)) return } - //TODO: validate data ;) - u := &entity.User{ - ID: entity.NewID(), - Email: input.Email, - Password: input.Password, - FirstName: input.FirstName, - LastName: input.LastName, - CreatedAt: time.Now(), - } - u.ID, err = service.CreateUser(u) + id, err := service.CreateUser(input.Email, input.Password, input.FirstName, input.LastName) if err != nil { log.Println(err.Error()) w.WriteHeader(http.StatusInternalServerError) @@ -91,15 +81,14 @@ func createUser(service user.UseCase) http.Handler { return } toJ := &presenter.User{ - ID: u.ID, - Email: u.Email, - FirstName: u.FirstName, - LastName: u.LastName, + ID: id, + Email: input.Email, + FirstName: input.FirstName, + LastName: input.LastName, } w.WriteHeader(http.StatusCreated) if err := json.NewEncoder(w).Encode(toJ); err != nil { - log.Println(err.Error()) w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return diff --git a/api/handler/user/user_test.go b/api/handler/user/user_test.go index 9f60833..0e39c4c 100644 --- a/api/handler/user/user_test.go +++ b/api/handler/user/user_test.go @@ -28,7 +28,9 @@ func Test_listUsers(t *testing.T) { path, err := r.GetRoute("listUsers").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user", path) - u := entity.NewFixtureUser() + u := &entity.User{ + ID: entity.NewID(), + } m.EXPECT(). ListUsers(). Return([]*entity.User{u}, nil) @@ -57,7 +59,9 @@ func Test_listUsers_Search(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() m := mock.NewMockUseCase(controller) - u := entity.NewFixtureUser() + u := &entity.User{ + ID: entity.NewID(), + } m.EXPECT(). SearchUsers("ozzy"). Return([]*entity.User{u}, nil) @@ -80,7 +84,7 @@ func Test_createUser(t *testing.T) { assert.Equal(t, "/v1/user", path) m.EXPECT(). - CreateUser(gomock.Any()). + CreateUser(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(entity.NewID(), nil) h := createUser(m) @@ -111,7 +115,9 @@ func Test_getUser(t *testing.T) { path, err := r.GetRoute("getUser").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user/{id}", path) - u := entity.NewFixtureUser() + u := &entity.User{ + ID: entity.NewID(), + } m.EXPECT(). GetUser(u.ID). Return(u, nil) @@ -138,7 +144,9 @@ func Test_deleteUser(t *testing.T) { path, err := r.GetRoute("deleteUser").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user/{id}", path) - u := entity.NewFixtureUser() + u := &entity.User{ + ID: entity.NewID(), + } m.EXPECT().DeleteUser(u.ID).Return(nil) handler := deleteUser(m) req, _ := http.NewRequest("DELETE", "/v1/user/"+u.ID.String(), nil) diff --git a/api/main.go b/api/main.go index cb11ac2..d76195c 100644 --- a/api/main.go +++ b/api/main.go @@ -14,8 +14,6 @@ import ( brepo "github.com/eminetto/clean-architecture-go-v2/infra/repository/book" urepo "github.com/eminetto/clean-architecture-go-v2/infra/repository/user" - "github.com/eminetto/clean-architecture-go-v2/pkg/password" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/loan" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -45,7 +43,7 @@ func main() { bookService := book.NewService(bookRepo) userRepo := urepo.NewMySQLRepoRepository(db) - userService := user.NewService(userRepo, password.NewService()) + userService := user.NewService(userRepo) loanUseCase := loan.NewService(userService, bookService) diff --git a/cmd/main.go b/cmd/main.go index cddf8c5..eabc3d9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -42,8 +42,8 @@ func main() { } defer db.Close() repo := repo.NewMySQLRepository(db) - manager := book.NewService(repo) - all, err := manager.SearchBooks(query) + service := book.NewService(repo) + all, err := service.SearchBooks(query) if err != nil { log.Fatal(err) } diff --git a/domain/entity/book.go b/domain/entity/book.go index 9a5fc0c..f1abac2 100644 --- a/domain/entity/book.go +++ b/domain/entity/book.go @@ -2,6 +2,8 @@ package entity import ( "time" + + "github.com/eminetto/clean-architecture-go-v2/domain" ) //Book data @@ -14,3 +16,24 @@ type Book struct { CreatedAt time.Time UpdatedAt time.Time } + +//NewBook create a new book +func NewBook(title string, author string, pages int, quantity int) (*Book, error) { + b := &Book{ + ID: NewID(), + Title: title, + Author: author, + Pages: pages, + Quantity: quantity, + CreatedAt: time.Now(), + } + return b, nil +} + +//Validate validate book +func (b *Book) Validate() error { + if b.Title == "" || b.Author == "" || b.Pages <= 0 { + return domain.ErrInvalidEntity + } + return nil +} diff --git a/domain/entity/book_fixture.go b/domain/entity/book_fixture.go deleted file mode 100644 index ce7fe1d..0000000 --- a/domain/entity/book_fixture.go +++ /dev/null @@ -1,16 +0,0 @@ -package entity - -import ( - "time" -) - -func NewFixtureBook() *Book { - return &Book{ - ID: NewID(), - Title: "I Am Ozzy", - Author: "Ozzy Osbourne", - Pages: 294, - Quantity: 1, - CreatedAt: time.Now(), - } -} diff --git a/domain/entity/book_test.go b/domain/entity/book_test.go new file mode 100644 index 0000000..9e6e2ea --- /dev/null +++ b/domain/entity/book_test.go @@ -0,0 +1,71 @@ +package entity_test + +import ( + "testing" + + "github.com/eminetto/clean-architecture-go-v2/domain" + "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/stretchr/testify/assert" +) + +func TestNewBook(t *testing.T) { + b, err := entity.NewBook("American Gods", "Neil Gaiman", 100, 0) + assert.Nil(t, err) + assert.Equal(t, b.Title, "American Gods") + assert.NotNil(t, b.ID) +} + +func TestBookValidate(t *testing.T) { + type test struct { + title string + author string + pages int + quantity int + want error + } + + tests := []test{ + { + title: "American Gods", + author: "Neil Gaiman", + pages: 100, + quantity: 1, + want: nil, + }, + { + title: "American Gods", + author: "Neil Gaiman", + pages: 100, + quantity: 0, + want: nil, + }, + { + title: "", + author: "Neil Gaiman", + pages: 100, + quantity: 1, + want: domain.ErrInvalidEntity, + }, + { + title: "American Gods", + author: "", + pages: 100, + quantity: 1, + want: domain.ErrInvalidEntity, + }, + { + title: "American Gods", + author: "Neil Gaiman", + pages: 0, + quantity: 1, + want: domain.ErrInvalidEntity, + }, + } + for _, tc := range tests { + + b, err := entity.NewBook(tc.title, tc.author, tc.pages, tc.quantity) + err = b.Validate() + assert.Equal(t, err, tc.want) + } + +} diff --git a/domain/entity/user.go b/domain/entity/user.go index 944debf..2401864 100644 --- a/domain/entity/user.go +++ b/domain/entity/user.go @@ -2,6 +2,9 @@ package entity import ( "time" + + "github.com/eminetto/clean-architecture-go-v2/domain" + "golang.org/x/crypto/bcrypt" ) //User data @@ -15,3 +18,67 @@ type User struct { UpdatedAt time.Time Books []ID } + +func NewUser(email, password, firstName, lastName string) (*User, error) { + e := &User{ + ID: NewID(), + Email: email, + FirstName: firstName, + LastName: lastName, + CreatedAt: time.Now(), + } + pwd, err := generatePassword(password) + if err != nil { + return nil, err + } + e.Password = pwd + return e, nil +} + +func (u *User) AddBook(id ID) error { + u.Books = append(u.Books, id) + return nil +} + +func (u *User) RemoveBook(id ID) error { + for i, j := range u.Books { + if j == id { + u.Books = append(u.Books[:i], u.Books[i+1:]...) + return nil + } + } + return domain.ErrNotFound +} + +func (u *User) GetBook(id ID) (ID, error) { + for _, v := range u.Books { + if v == id { + return id, nil + } + } + return id, domain.ErrNotFound +} + +func (u *User) Validate() error { + if u.Email == "" || u.FirstName == "" || u.LastName == "" || u.Password == "" { + return domain.ErrInvalidEntity + } + + return nil +} + +func (u *User) ValidatePassword(p string) error { + err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(p)) + if err != nil { + return err + } + return nil +} + +func generatePassword(raw string) (string, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(raw), 10) + if err != nil { + return "", err + } + return string(hash), nil +} diff --git a/domain/entity/user_fixture.go b/domain/entity/user_fixture.go deleted file mode 100644 index d83f996..0000000 --- a/domain/entity/user_fixture.go +++ /dev/null @@ -1,16 +0,0 @@ -package entity - -import ( - "time" -) - -func NewFixtureUser() *User { - return &User{ - ID: NewID(), - Email: "ozzy@metalgods.net", - Password: "123456", - FirstName: "Ozzy", - LastName: "Osbourne", - CreatedAt: time.Now(), - } -} diff --git a/domain/entity/user_test.go b/domain/entity/user_test.go new file mode 100644 index 0000000..86da9a9 --- /dev/null +++ b/domain/entity/user_test.go @@ -0,0 +1,109 @@ +package entity_test + +import ( + "testing" + + "github.com/eminetto/clean-architecture-go-v2/domain" + "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/stretchr/testify/assert" +) + +func TestNewUser(t *testing.T) { + u, err := entity.NewUser("sjobs@apple.com", "new_password", "Steve", "Jobs") + assert.Nil(t, err) + assert.Equal(t, u.FirstName, "Steve") + assert.NotNil(t, u.ID) + assert.NotEqual(t, u.Password, "new_password") +} + +func TestValidatePassword(t *testing.T) { + u, _ := entity.NewUser("sjobs@apple.com", "new_password", "Steve", "Jobs") + err := u.ValidatePassword("new_password") + assert.Nil(t, err) + err = u.ValidatePassword("wrong_password") + assert.NotNil(t, err) + +} + +func TestAddBook(t *testing.T) { + u, _ := entity.NewUser("sjobs@apple.com", "new_password", "Steve", "Jobs") + err := u.AddBook(entity.NewID()) + assert.Nil(t, err) + assert.Equal(t, 1, len(u.Books)) +} + +func TestRemoveBook(t *testing.T) { + u, _ := entity.NewUser("sjobs@apple.com", "new_password", "Steve", "Jobs") + err := u.RemoveBook(entity.NewID()) + assert.Equal(t, domain.ErrNotFound, err) + bID := entity.NewID() + _ = u.AddBook(bID) + err = u.RemoveBook(bID) + assert.Nil(t, err) +} + +func TestGetBook(t *testing.T) { + u, _ := entity.NewUser("sjobs@apple.com", "new_password", "Steve", "Jobs") + bID := entity.NewID() + _ = u.AddBook(bID) + id, err := u.GetBook(bID) + assert.Nil(t, err) + assert.Equal(t, id, bID) + _, err = u.GetBook(entity.NewID()) + assert.Equal(t, domain.ErrNotFound, err) +} + +func TestUserValidate(t *testing.T) { + type test struct { + email string + password string + firstName string + lastName string + want error + } + + tests := []test{ + { + email: "sjobs@apple.com", + password: "new_password", + firstName: "Steve", + lastName: "Jobs", + want: nil, + }, + { + email: "", + password: "new_password", + firstName: "Steve", + lastName: "Jobs", + want: domain.ErrInvalidEntity, + }, + { + email: "sjobs@apple.com", + password: "", + firstName: "Steve", + lastName: "Jobs", + want: nil, + }, + { + email: "sjobs@apple.com", + password: "new_password", + firstName: "", + lastName: "Jobs", + want: domain.ErrInvalidEntity, + }, + { + email: "sjobs@apple.com", + password: "new_password", + firstName: "Steve", + lastName: "", + want: domain.ErrInvalidEntity, + }, + } + for _, tc := range tests { + + u, err := entity.NewUser(tc.email, tc.password, tc.firstName, tc.lastName) + err = u.Validate() + assert.Equal(t, err, tc.want) + } + +} diff --git a/domain/error.go b/domain/error.go index e235ef3..c86196b 100644 --- a/domain/error.go +++ b/domain/error.go @@ -5,6 +5,9 @@ import "errors" //ErrNotFound not found var ErrNotFound = errors.New("Not found") +//ErrInvalidEntity invalid entity +var ErrInvalidEntity = errors.New("Invalid entity") + //ErrCannotBeDeleted cannot be deleted var ErrCannotBeDeleted = errors.New("Cannot Be Deleted") diff --git a/domain/usecase/book/interface.go b/domain/usecase/book/interface.go index 0382f8a..07cdb00 100644 --- a/domain/usecase/book/interface.go +++ b/domain/usecase/book/interface.go @@ -29,7 +29,7 @@ type UseCase interface { GetBook(id entity.ID) (*entity.Book, error) SearchBooks(query string) ([]*entity.Book, error) ListBooks() ([]*entity.Book, error) - CreateBook(e *entity.Book) (entity.ID, error) + CreateBook(title string, author string, pages int, quantity int) (entity.ID, error) UpdateBook(e *entity.Book) error DeleteBook(id entity.ID) error } diff --git a/domain/usecase/book/mock/book.go b/domain/usecase/book/mock/book.go index 932c503..bb72af6 100644 --- a/domain/usecase/book/mock/book.go +++ b/domain/usecase/book/mock/book.go @@ -5,9 +5,10 @@ package mock import ( + reflect "reflect" + entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" gomock "github.com/golang/mock/gomock" - reflect "reflect" ) // MockReader is a mock of Reader interface @@ -324,18 +325,18 @@ func (mr *MockUseCaseMockRecorder) ListBooks() *gomock.Call { } // CreateBook mocks base method -func (m *MockUseCase) CreateBook(e *entity.Book) (entity.ID, error) { +func (m *MockUseCase) CreateBook(title, author string, pages, quantity int) (entity.ID, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateBook", e) + ret := m.ctrl.Call(m, "CreateBook", title, author, pages, quantity) ret0, _ := ret[0].(entity.ID) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateBook indicates an expected call of CreateBook -func (mr *MockUseCaseMockRecorder) CreateBook(e interface{}) *gomock.Call { +func (mr *MockUseCaseMockRecorder) CreateBook(title, author, pages, quantity interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBook", reflect.TypeOf((*MockUseCase)(nil).CreateBook), e) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBook", reflect.TypeOf((*MockUseCase)(nil).CreateBook), title, author, pages, quantity) } // UpdateBook mocks base method diff --git a/domain/usecase/book/service.go b/domain/usecase/book/service.go index 172727c..36f5ed7 100644 --- a/domain/usecase/book/service.go +++ b/domain/usecase/book/service.go @@ -22,10 +22,16 @@ func NewService(r Repository) *Service { } //CreateBook create a book -func (s *Service) CreateBook(e *entity.Book) (entity.ID, error) { - e.ID = entity.NewID() - e.CreatedAt = time.Now() - return s.repo.Create(e) +func (s *Service) CreateBook(title string, author string, pages int, quantity int) (entity.ID, error) { + b, err := entity.NewBook(title, author, pages, quantity) + if err != nil { + return b.ID, err + } + err = b.Validate() + if err != nil { + return b.ID, err + } + return s.repo.Create(b) } //GetBook get a book @@ -76,5 +82,10 @@ func (s *Service) DeleteBook(id entity.ID) error { //UpdateBook Update a book func (s *Service) UpdateBook(e *entity.Book) error { + err := e.Validate() + if err != nil { + return err + } + e.UpdatedAt = time.Now() return s.repo.Update(e) } diff --git a/domain/usecase/book/service_test.go b/domain/usecase/book/service_test.go index 479db7f..9db7d39 100644 --- a/domain/usecase/book/service_test.go +++ b/domain/usecase/book/service_test.go @@ -2,6 +2,7 @@ package book import ( "testing" + "time" "github.com/eminetto/clean-architecture-go-v2/domain/entity" @@ -12,25 +13,34 @@ import ( "github.com/stretchr/testify/assert" ) +func newFixtureBook() *entity.Book { + return &entity.Book{ + Title: "I Am Ozzy", + Author: "Ozzy Osbourne", + Pages: 294, + Quantity: 1, + CreatedAt: time.Now(), + } +} + func Test_Create(t *testing.T) { repo := book.NewInmemRepository() m := NewService(repo) - u := entity.NewFixtureBook() - id, err := m.CreateBook(u) + u := newFixtureBook() + _, err := m.CreateBook(u.Title, u.Author, u.Pages, u.Quantity) assert.Nil(t, err) - assert.Equal(t, u.ID, id) assert.False(t, u.CreatedAt.IsZero()) } func Test_SearchAndFind(t *testing.T) { repo := book.NewInmemRepository() m := NewService(repo) - u1 := entity.NewFixtureBook() - u2 := entity.NewFixtureBook() + u1 := newFixtureBook() + u2 := newFixtureBook() u2.Title = "Lemmy: Biography" - uID, _ := m.CreateBook(u1) - _, _ = m.CreateBook(u2) + uID, _ := m.CreateBook(u1.Title, u1.Author, u1.Pages, u1.Quantity) + _, _ = m.CreateBook(u2.Title, u2.Author, u2.Pages, u2.Quantity) t.Run("search", func(t *testing.T) { c, err := m.SearchBooks("ozzy") @@ -58,8 +68,8 @@ func Test_SearchAndFind(t *testing.T) { func Test_Update(t *testing.T) { repo := book.NewInmemRepository() m := NewService(repo) - u := entity.NewFixtureBook() - id, err := m.CreateBook(u) + u := newFixtureBook() + id, err := m.CreateBook(u.Title, u.Author, u.Pages, u.Quantity) assert.Nil(t, err) saved, _ := m.GetBook(id) saved.Title = "Lemmy: Biography" @@ -72,9 +82,9 @@ func Test_Update(t *testing.T) { func TestDelete(t *testing.T) { repo := book.NewInmemRepository() m := NewService(repo) - u1 := entity.NewFixtureBook() - u2 := entity.NewFixtureBook() - u2ID, _ := m.CreateBook(u2) + u1 := newFixtureBook() + u2 := newFixtureBook() + u2ID, _ := m.CreateBook(u2.Title, u2.Author, u2.Pages, u2.Quantity) err := m.DeleteBook(u1.ID) assert.Equal(t, domain.ErrNotFound, err) diff --git a/domain/usecase/loan/mock/loan.go b/domain/usecase/loan/mock/loan.go index 0d25a74..5d5b355 100644 --- a/domain/usecase/loan/mock/loan.go +++ b/domain/usecase/loan/mock/loan.go @@ -5,9 +5,10 @@ package mock import ( + reflect "reflect" + entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" gomock "github.com/golang/mock/gomock" - reflect "reflect" ) // MockUseCase is a mock of UseCase interface diff --git a/domain/usecase/loan/service.go b/domain/usecase/loan/service.go index 18e684a..1bb3e3a 100644 --- a/domain/usecase/loan/service.go +++ b/domain/usecase/loan/service.go @@ -34,12 +34,15 @@ func (s *Service) Borrow(u *entity.User, b *entity.Book) error { if b.Quantity <= 0 { return domain.ErrNotEnoughBooks } - for _, v := range u.Books { - if v == b.ID { - return domain.ErrBookAlreadyBorrowed - } + + _, err = u.GetBook(b.ID) + if err == nil { + return domain.ErrBookAlreadyBorrowed + } + err = u.AddBook(b.ID) + if err != nil { + return err } - u.Books = append(u.Books, b.ID) err = s.userService.UpdateUser(u) if err != nil { return err @@ -66,13 +69,13 @@ func (s *Service) Return(b *entity.Book) error { borrowed := false var borrowedBy entity.ID for _, u := range all { - for _, bookID := range u.Books { - if bookID == b.ID { - borrowed = true - borrowedBy = u.ID - break - } + _, err := u.GetBook(b.ID) + if err != nil { + continue } + borrowed = true + borrowedBy = u.ID + break } if !borrowed { return domain.ErrBookNotBorrowed @@ -81,15 +84,13 @@ func (s *Service) Return(b *entity.Book) error { if err != nil { return err } - for i, j := range u.Books { - if j == b.ID { - u.Books = append(u.Books[:i], u.Books[i+1:]...) - err = s.userService.UpdateUser(u) - if err != nil { - return err - } - break - } + err = u.RemoveBook(b.ID) + if err != nil { + return err + } + err = s.userService.UpdateUser(u) + if err != nil { + return err } b.Quantity++ err = s.bookService.UpdateBook(b) diff --git a/domain/usecase/loan/service_test.go b/domain/usecase/loan/service_test.go index 663d7ba..cd68a2a 100644 --- a/domain/usecase/loan/service_test.go +++ b/domain/usecase/loan/service_test.go @@ -19,23 +19,35 @@ func Test_Borrow(t *testing.T) { bMock := bmock.NewMockUseCase(controller) uc := NewService(uMock, bMock) t.Run("user not found", func(t *testing.T) { - u := entity.NewFixtureUser() - b := entity.NewFixtureBook() + u := &entity.User{ + ID: entity.NewID(), + } + b := &entity.Book{ + ID: entity.NewID(), + } uMock.EXPECT().GetUser(u.ID).Return(nil, domain.ErrNotFound) err := uc.Borrow(u, b) assert.Equal(t, domain.ErrNotFound, err) }) t.Run("book not found", func(t *testing.T) { - u := entity.NewFixtureUser() - b := entity.NewFixtureBook() + u := &entity.User{ + ID: entity.NewID(), + } + b := &entity.Book{ + ID: entity.NewID(), + } uMock.EXPECT().GetUser(u.ID).Return(u, nil) bMock.EXPECT().GetBook(b.ID).Return(nil, domain.ErrNotFound) err := uc.Borrow(u, b) assert.Equal(t, domain.ErrNotFound, err) }) t.Run("not enough books to borrow", func(t *testing.T) { - u := entity.NewFixtureUser() - b := entity.NewFixtureBook() + u := &entity.User{ + ID: entity.NewID(), + } + b := &entity.Book{ + ID: entity.NewID(), + } b.Quantity = 0 uMock.EXPECT().GetUser(u.ID).Return(u, nil) bMock.EXPECT().GetBook(b.ID).Return(b, nil) @@ -43,9 +55,13 @@ func Test_Borrow(t *testing.T) { assert.Equal(t, domain.ErrNotEnoughBooks, err) }) t.Run("book already borrowed", func(t *testing.T) { - u := entity.NewFixtureUser() - b := entity.NewFixtureBook() - u.Books = []entity.ID{b.ID} + u := &entity.User{ + ID: entity.NewID(), + } + b := &entity.Book{ + ID: entity.NewID(), + } + u.AddBook(b.ID) b.Quantity = 1 uMock.EXPECT().GetUser(u.ID).Return(u, nil) bMock.EXPECT().GetBook(b.ID).Return(b, nil) @@ -53,8 +69,13 @@ func Test_Borrow(t *testing.T) { assert.Equal(t, domain.ErrBookAlreadyBorrowed, err) }) t.Run("sucess", func(t *testing.T) { - u := entity.NewFixtureUser() - b := entity.NewFixtureBook() + u := &entity.User{ + ID: entity.NewID(), + } + b := &entity.Book{ + ID: entity.NewID(), + Quantity: 10, + } uMock.EXPECT().GetUser(u.ID).Return(u, nil) bMock.EXPECT().GetBook(b.ID).Return(b, nil) uMock.EXPECT().UpdateUser(u).Return(nil) @@ -71,23 +92,33 @@ func Test_Return(t *testing.T) { bMock := bmock.NewMockUseCase(controller) uc := NewService(uMock, bMock) t.Run("book not found", func(t *testing.T) { - b := entity.NewFixtureBook() + b := &entity.Book{ + ID: entity.NewID(), + } bMock.EXPECT().GetBook(b.ID).Return(nil, domain.ErrNotFound) err := uc.Return(b) assert.Equal(t, domain.ErrNotFound, err) }) t.Run("book not borrowed", func(t *testing.T) { - u := entity.NewFixtureUser() - b := entity.NewFixtureBook() + u := &entity.User{ + ID: entity.NewID(), + } + b := &entity.Book{ + ID: entity.NewID(), + } bMock.EXPECT().GetBook(b.ID).Return(b, nil) uMock.EXPECT().ListUsers().Return([]*entity.User{u}, nil) err := uc.Return(b) assert.Equal(t, domain.ErrBookNotBorrowed, err) }) t.Run("success", func(t *testing.T) { - u := entity.NewFixtureUser() - b := entity.NewFixtureBook() - u.Books = []entity.ID{b.ID} + u := &entity.User{ + ID: entity.NewID(), + } + b := &entity.Book{ + ID: entity.NewID(), + } + u.AddBook(b.ID) bMock.EXPECT().GetBook(b.ID).Return(b, nil) uMock.EXPECT().GetUser(u.ID).Return(u, nil) uMock.EXPECT().ListUsers().Return([]*entity.User{u}, nil) diff --git a/domain/usecase/user/interface.go b/domain/usecase/user/interface.go index 454af47..f673510 100644 --- a/domain/usecase/user/interface.go +++ b/domain/usecase/user/interface.go @@ -29,7 +29,7 @@ type UseCase interface { GetUser(id entity.ID) (*entity.User, error) SearchUsers(query string) ([]*entity.User, error) ListUsers() ([]*entity.User, error) - CreateUser(e *entity.User) (entity.ID, error) + CreateUser(email, password, firstName, lastName string) (entity.ID, error) UpdateUser(e *entity.User) error DeleteUser(id entity.ID) error } diff --git a/domain/usecase/user/mock/user.go b/domain/usecase/user/mock/user.go index a184132..adcd3e7 100644 --- a/domain/usecase/user/mock/user.go +++ b/domain/usecase/user/mock/user.go @@ -5,9 +5,10 @@ package mock import ( + reflect "reflect" + entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" gomock "github.com/golang/mock/gomock" - reflect "reflect" ) // MockReader is a mock of Reader interface @@ -324,18 +325,18 @@ func (mr *MockUseCaseMockRecorder) ListUsers() *gomock.Call { } // CreateUser mocks base method -func (m *MockUseCase) CreateUser(e *entity.User) (entity.ID, error) { +func (m *MockUseCase) CreateUser(email, password, firstName, lastName string) (entity.ID, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateUser", e) + ret := m.ctrl.Call(m, "CreateUser", email, password, firstName, lastName) ret0, _ := ret[0].(entity.ID) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateUser indicates an expected call of CreateUser -func (mr *MockUseCaseMockRecorder) CreateUser(e interface{}) *gomock.Call { +func (mr *MockUseCaseMockRecorder) CreateUser(email, password, firstName, lastName interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockUseCase)(nil).CreateUser), e) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockUseCase)(nil).CreateUser), email, password, firstName, lastName) } // UpdateUser mocks base method diff --git a/domain/usecase/user/service.go b/domain/usecase/user/service.go index 5fa50e9..dbb960d 100644 --- a/domain/usecase/user/service.go +++ b/domain/usecase/user/service.go @@ -6,34 +6,31 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/pkg/password" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) //Service interface type Service struct { repo Repository - pwd password.Service } //NewService create new use case -func NewService(r Repository, pwd password.Service) *Service { +func NewService(r Repository) *Service { return &Service{ repo: r, - pwd: pwd, } } //CreateUser Create an user -func (s *Service) CreateUser(e *entity.User) (entity.ID, error) { - e.ID = entity.NewID() - e.CreatedAt = time.Now() - pwd, err := s.pwd.Generate(e.Password) +func (s *Service) CreateUser(email, password, firstName, lastName string) (entity.ID, error) { + e, err := entity.NewUser(email, password, firstName, lastName) if err != nil { - return e.ID, err + return e.ID, domain.ErrInvalidEntity + } + err = e.Validate() + if err != nil { + return e.ID, domain.ErrInvalidEntity } - e.Password = pwd return s.repo.Create(e) } @@ -69,6 +66,10 @@ func (s *Service) DeleteUser(id entity.ID) error { //UpdateUser Update an user func (s *Service) UpdateUser(e *entity.User) error { + err := e.Validate() + if err != nil { + return domain.ErrInvalidEntity + } e.UpdatedAt = time.Now() return s.repo.Update(e) } diff --git a/domain/usecase/user/service_test.go b/domain/usecase/user/service_test.go index 6a9fb8e..c41b42c 100644 --- a/domain/usecase/user/service_test.go +++ b/domain/usecase/user/service_test.go @@ -2,37 +2,46 @@ package user import ( "testing" + "time" "github.com/eminetto/clean-architecture-go-v2/infra/repository/user" - "github.com/eminetto/clean-architecture-go-v2/pkg/password" - "github.com/eminetto/clean-architecture-go-v2/domain" "github.com/eminetto/clean-architecture-go-v2/domain/entity" "github.com/stretchr/testify/assert" ) +func newFixtureUser() *entity.User { + return &entity.User{ + ID: entity.NewID(), + Email: "ozzy@metalgods.net", + Password: "123456", + FirstName: "Ozzy", + LastName: "Osbourne", + CreatedAt: time.Now(), + } +} + func Test_Create(t *testing.T) { repo := user.NewInmemRepository() - m := NewService(repo, password.NewFakeService()) - u := entity.NewFixtureUser() - id, err := m.CreateUser(u) + m := NewService(repo) + u := newFixtureUser() + _, err := m.CreateUser(u.Email, u.Password, u.FirstName, u.LastName) assert.Nil(t, err) - assert.Equal(t, u.ID, id) assert.False(t, u.CreatedAt.IsZero()) assert.True(t, u.UpdatedAt.IsZero()) } func Test_SearchAndFind(t *testing.T) { repo := user.NewInmemRepository() - m := NewService(repo, password.NewFakeService()) - u1 := entity.NewFixtureUser() - u2 := entity.NewFixtureUser() + m := NewService(repo) + u1 := newFixtureUser() + u2 := newFixtureUser() u2.FirstName = "Lemmy" - uID, _ := m.CreateUser(u1) - _, _ = m.CreateUser(u2) + uID, _ := m.CreateUser(u1.Email, u1.Password, u1.FirstName, u1.LastName) + _, _ = m.CreateUser(u2.Email, u2.Password, u2.FirstName, u2.LastName) t.Run("search", func(t *testing.T) { c, err := m.SearchUsers("ozzy") @@ -59,9 +68,9 @@ func Test_SearchAndFind(t *testing.T) { func Test_Update(t *testing.T) { repo := user.NewInmemRepository() - m := NewService(repo, password.NewFakeService()) - u := entity.NewFixtureUser() - id, err := m.CreateUser(u) + m := NewService(repo) + u := newFixtureUser() + id, err := m.CreateUser(u.Email, u.Password, u.FirstName, u.LastName) assert.Nil(t, err) saved, _ := m.GetUser(id) saved.FirstName = "Dio" @@ -70,16 +79,16 @@ func Test_Update(t *testing.T) { updated, err := m.GetUser(id) assert.Nil(t, err) assert.Equal(t, "Dio", updated.FirstName) - assert.False(t, u.UpdatedAt.IsZero()) + assert.False(t, updated.UpdatedAt.IsZero()) assert.Equal(t, 1, len(updated.Books)) } func TestDelete(t *testing.T) { repo := user.NewInmemRepository() - m := NewService(repo, password.NewFakeService()) - u1 := entity.NewFixtureUser() - u2 := entity.NewFixtureUser() - u2ID, _ := m.CreateUser(u2) + m := NewService(repo) + u1 := newFixtureUser() + u2 := newFixtureUser() + u2ID, _ := m.CreateUser(u2.Email, u2.Password, u2.FirstName, u2.LastName) err := m.DeleteUser(u1.ID) assert.Equal(t, domain.ErrNotFound, err) @@ -89,9 +98,11 @@ func TestDelete(t *testing.T) { _, err = m.GetUser(u2ID) assert.Equal(t, domain.ErrNotFound, err) - u3 := entity.NewFixtureUser() - u3.Books = []entity.ID{entity.NewID()} - _, _ = m.CreateUser(u3) - err = m.DeleteUser(u3.ID) + u3 := newFixtureUser() + id, _ := m.CreateUser(u3.Email, u3.Password, u3.FirstName, u3.LastName) + saved, _ := m.GetUser(id) + saved.Books = []entity.ID{entity.NewID()} + _ = m.UpdateUser(saved) + err = m.DeleteUser(id) assert.Equal(t, domain.ErrCannotBeDeleted, err) } From 83c00526b2b95239e87a8c74b563a9196ab38b42 Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Wed, 7 Oct 2020 22:57:54 -0300 Subject: [PATCH 07/12] feat: improvements in entity domain logic --- domain/entity/book.go | 6 +++++- domain/entity/book_test.go | 7 +++---- domain/entity/user.go | 20 +++++++++++++++++--- domain/entity/user_test.go | 9 ++++++--- domain/usecase/book/mock/book.go | 3 +-- domain/usecase/book/service.go | 4 ---- domain/usecase/loan/mock/loan.go | 3 +-- domain/usecase/loan/service.go | 4 ---- domain/usecase/user/mock/user.go | 3 +-- domain/usecase/user/service.go | 6 +----- 10 files changed, 35 insertions(+), 30 deletions(-) diff --git a/domain/entity/book.go b/domain/entity/book.go index f1abac2..b1e0502 100644 --- a/domain/entity/book.go +++ b/domain/entity/book.go @@ -27,12 +27,16 @@ func NewBook(title string, author string, pages int, quantity int) (*Book, error Quantity: quantity, CreatedAt: time.Now(), } + err := b.Validate() + if err != nil { + return nil, domain.ErrInvalidEntity + } return b, nil } //Validate validate book func (b *Book) Validate() error { - if b.Title == "" || b.Author == "" || b.Pages <= 0 { + if b.Title == "" || b.Author == "" || b.Pages <= 0 || b.Quantity <= 0 { return domain.ErrInvalidEntity } return nil diff --git a/domain/entity/book_test.go b/domain/entity/book_test.go index 9e6e2ea..a383e98 100644 --- a/domain/entity/book_test.go +++ b/domain/entity/book_test.go @@ -9,7 +9,7 @@ import ( ) func TestNewBook(t *testing.T) { - b, err := entity.NewBook("American Gods", "Neil Gaiman", 100, 0) + b, err := entity.NewBook("American Gods", "Neil Gaiman", 100, 1) assert.Nil(t, err) assert.Equal(t, b.Title, "American Gods") assert.NotNil(t, b.ID) @@ -37,7 +37,7 @@ func TestBookValidate(t *testing.T) { author: "Neil Gaiman", pages: 100, quantity: 0, - want: nil, + want: domain.ErrInvalidEntity, }, { title: "", @@ -63,8 +63,7 @@ func TestBookValidate(t *testing.T) { } for _, tc := range tests { - b, err := entity.NewBook(tc.title, tc.author, tc.pages, tc.quantity) - err = b.Validate() + _, err := entity.NewBook(tc.title, tc.author, tc.pages, tc.quantity) assert.Equal(t, err, tc.want) } diff --git a/domain/entity/user.go b/domain/entity/user.go index 2401864..b182723 100644 --- a/domain/entity/user.go +++ b/domain/entity/user.go @@ -19,8 +19,9 @@ type User struct { Books []ID } +//NewUser create a new user func NewUser(email, password, firstName, lastName string) (*User, error) { - e := &User{ + u := &User{ ID: NewID(), Email: email, FirstName: firstName, @@ -31,15 +32,25 @@ func NewUser(email, password, firstName, lastName string) (*User, error) { if err != nil { return nil, err } - e.Password = pwd - return e, nil + u.Password = pwd + err = u.Validate() + if err != nil { + return nil, domain.ErrInvalidEntity + } + return u, nil } +//AddBook add a book func (u *User) AddBook(id ID) error { + _, err := u.GetBook(id) + if err == nil { + return domain.ErrBookAlreadyBorrowed + } u.Books = append(u.Books, id) return nil } +//RemoveBook remove a book func (u *User) RemoveBook(id ID) error { for i, j := range u.Books { if j == id { @@ -50,6 +61,7 @@ func (u *User) RemoveBook(id ID) error { return domain.ErrNotFound } +//GetBook get a book func (u *User) GetBook(id ID) (ID, error) { for _, v := range u.Books { if v == id { @@ -59,6 +71,7 @@ func (u *User) GetBook(id ID) (ID, error) { return id, domain.ErrNotFound } +//Validate validate data func (u *User) Validate() error { if u.Email == "" || u.FirstName == "" || u.LastName == "" || u.Password == "" { return domain.ErrInvalidEntity @@ -67,6 +80,7 @@ func (u *User) Validate() error { return nil } +//ValidatePassword func (u *User) ValidatePassword(p string) error { err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(p)) if err != nil { diff --git a/domain/entity/user_test.go b/domain/entity/user_test.go index 86da9a9..9c16439 100644 --- a/domain/entity/user_test.go +++ b/domain/entity/user_test.go @@ -27,9 +27,13 @@ func TestValidatePassword(t *testing.T) { func TestAddBook(t *testing.T) { u, _ := entity.NewUser("sjobs@apple.com", "new_password", "Steve", "Jobs") - err := u.AddBook(entity.NewID()) + bID := entity.NewID() + err := u.AddBook(bID) assert.Nil(t, err) assert.Equal(t, 1, len(u.Books)) + err = u.AddBook(bID) + assert.Equal(t, domain.ErrBookAlreadyBorrowed, err) + } func TestRemoveBook(t *testing.T) { @@ -101,8 +105,7 @@ func TestUserValidate(t *testing.T) { } for _, tc := range tests { - u, err := entity.NewUser(tc.email, tc.password, tc.firstName, tc.lastName) - err = u.Validate() + _, err := entity.NewUser(tc.email, tc.password, tc.firstName, tc.lastName) assert.Equal(t, err, tc.want) } diff --git a/domain/usecase/book/mock/book.go b/domain/usecase/book/mock/book.go index bb72af6..4afe5e9 100644 --- a/domain/usecase/book/mock/book.go +++ b/domain/usecase/book/mock/book.go @@ -5,10 +5,9 @@ package mock import ( - reflect "reflect" - entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" gomock "github.com/golang/mock/gomock" + reflect "reflect" ) // MockReader is a mock of Reader interface diff --git a/domain/usecase/book/service.go b/domain/usecase/book/service.go index 36f5ed7..fdbcaf3 100644 --- a/domain/usecase/book/service.go +++ b/domain/usecase/book/service.go @@ -27,10 +27,6 @@ func (s *Service) CreateBook(title string, author string, pages int, quantity in if err != nil { return b.ID, err } - err = b.Validate() - if err != nil { - return b.ID, err - } return s.repo.Create(b) } diff --git a/domain/usecase/loan/mock/loan.go b/domain/usecase/loan/mock/loan.go index 5d5b355..0d25a74 100644 --- a/domain/usecase/loan/mock/loan.go +++ b/domain/usecase/loan/mock/loan.go @@ -5,10 +5,9 @@ package mock import ( - reflect "reflect" - entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" gomock "github.com/golang/mock/gomock" + reflect "reflect" ) // MockUseCase is a mock of UseCase interface diff --git a/domain/usecase/loan/service.go b/domain/usecase/loan/service.go index 1bb3e3a..edc6c09 100644 --- a/domain/usecase/loan/service.go +++ b/domain/usecase/loan/service.go @@ -35,10 +35,6 @@ func (s *Service) Borrow(u *entity.User, b *entity.Book) error { return domain.ErrNotEnoughBooks } - _, err = u.GetBook(b.ID) - if err == nil { - return domain.ErrBookAlreadyBorrowed - } err = u.AddBook(b.ID) if err != nil { return err diff --git a/domain/usecase/user/mock/user.go b/domain/usecase/user/mock/user.go index adcd3e7..94d58dd 100644 --- a/domain/usecase/user/mock/user.go +++ b/domain/usecase/user/mock/user.go @@ -5,10 +5,9 @@ package mock import ( - reflect "reflect" - entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" gomock "github.com/golang/mock/gomock" + reflect "reflect" ) // MockReader is a mock of Reader interface diff --git a/domain/usecase/user/service.go b/domain/usecase/user/service.go index dbb960d..e43861b 100644 --- a/domain/usecase/user/service.go +++ b/domain/usecase/user/service.go @@ -25,11 +25,7 @@ func NewService(r Repository) *Service { func (s *Service) CreateUser(email, password, firstName, lastName string) (entity.ID, error) { e, err := entity.NewUser(email, password, firstName, lastName) if err != nil { - return e.ID, domain.ErrInvalidEntity - } - err = e.Validate() - if err != nil { - return e.ID, domain.ErrInvalidEntity + return e.ID, err } return s.repo.Create(e) } From d4496c25855ff479c3a86e665d25f0b229e4a68e Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Mon, 12 Oct 2020 06:44:01 -0300 Subject: [PATCH 08/12] feat: improve handler structure --- api/handler/{book => }/book.go | 6 ++-- api/handler/{book => }/book_test.go | 46 ++++++++++++++--------------- api/handler/{loan => }/loan.go | 6 ++-- api/handler/{loan => }/loan_test.go | 6 ++-- api/handler/{user => }/user.go | 6 ++-- api/handler/{user => }/user_test.go | 10 +++---- api/main.go | 10 +++---- 7 files changed, 44 insertions(+), 46 deletions(-) rename api/handler/{book => }/book.go (97%) rename api/handler/{book => }/book_test.go (81%) rename api/handler/{loan => }/loan.go (94%) rename api/handler/{loan => }/loan_test.go (97%) rename api/handler/{user => }/user.go (97%) rename api/handler/{user => }/user_test.go (97%) diff --git a/api/handler/book/book.go b/api/handler/book.go similarity index 97% rename from api/handler/book/book.go rename to api/handler/book.go index 27577fe..65be03d 100644 --- a/api/handler/book/book.go +++ b/api/handler/book.go @@ -1,4 +1,4 @@ -package book +package handler import ( "encoding/json" @@ -152,8 +152,8 @@ func deleteBook(service book.UseCase) http.Handler { }) } -//MakeHandlers make url handlers -func MakeHandlers(r *mux.Router, n negroni.Negroni, service book.UseCase) { +//MakeBookHandlers make url handlers +func MakeBookHandlers(r *mux.Router, n negroni.Negroni, service book.UseCase) { r.Handle("/v1/book", n.With( negroni.Wrap(listBooks(service)), )).Methods("GET", "OPTIONS").Name("listBooks") diff --git a/api/handler/book/book_test.go b/api/handler/book_test.go similarity index 81% rename from api/handler/book/book_test.go rename to api/handler/book_test.go index 67cd612..c6103db 100644 --- a/api/handler/book/book_test.go +++ b/api/handler/book_test.go @@ -1,4 +1,4 @@ -package book +package handler import ( "encoding/json" @@ -21,20 +21,20 @@ import ( func Test_listBooks(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockUseCase(controller) + service := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeHandlers(r, *n, manager) + MakeBookHandlers(r, *n, service) path, err := r.GetRoute("listBooks").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book", path) b := &entity.Book{ ID: entity.NewID(), } - manager.EXPECT(). + service.EXPECT(). ListBooks(). Return([]*entity.Book{b}, nil) - ts := httptest.NewServer(listBooks(manager)) + ts := httptest.NewServer(listBooks(service)) defer ts.Close() res, err := http.Get(ts.URL) assert.Nil(t, err) @@ -44,10 +44,10 @@ func Test_listBooks(t *testing.T) { func Test_listBooks_NotFound(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockUseCase(controller) - ts := httptest.NewServer(listBooks(manager)) + service := mock.NewMockUseCase(controller) + ts := httptest.NewServer(listBooks(service)) defer ts.Close() - manager.EXPECT(). + service.EXPECT(). SearchBooks("book of books"). Return(nil, domain.ErrNotFound) res, err := http.Get(ts.URL + "?title=book+of+books") @@ -58,14 +58,14 @@ func Test_listBooks_NotFound(t *testing.T) { func Test_listBooks_Search(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockUseCase(controller) + service := mock.NewMockUseCase(controller) b := &entity.Book{ ID: entity.NewID(), } - manager.EXPECT(). + service.EXPECT(). SearchBooks("ozzy"). Return([]*entity.Book{b}, nil) - ts := httptest.NewServer(listBooks(manager)) + ts := httptest.NewServer(listBooks(service)) defer ts.Close() res, err := http.Get(ts.URL + "?title=ozzy") assert.Nil(t, err) @@ -75,18 +75,18 @@ func Test_listBooks_Search(t *testing.T) { func Test_createBook(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockUseCase(controller) + service := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeHandlers(r, *n, manager) + MakeBookHandlers(r, *n, service) path, err := r.GetRoute("createBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book", path) - manager.EXPECT(). + service.EXPECT(). CreateBook(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). Return(entity.NewID(), nil) - h := createBook(manager) + h := createBook(service) ts := httptest.NewServer(h) defer ts.Close() @@ -107,20 +107,20 @@ func Test_createBook(t *testing.T) { func Test_getBook(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockUseCase(controller) + service := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeHandlers(r, *n, manager) + MakeBookHandlers(r, *n, service) path, err := r.GetRoute("getBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book/{id}", path) b := &entity.Book{ ID: entity.NewID(), } - manager.EXPECT(). + service.EXPECT(). GetBook(b.ID). Return(b, nil) - handler := getBook(manager) + handler := getBook(service) r.Handle("/v1/book/{id}", handler) ts := httptest.NewServer(r) defer ts.Close() @@ -136,18 +136,18 @@ func Test_getBook(t *testing.T) { func Test_deleteBook(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - manager := mock.NewMockUseCase(controller) + service := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeHandlers(r, *n, manager) + MakeBookHandlers(r, *n, service) path, err := r.GetRoute("deleteBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/book/{id}", path) b := &entity.Book{ ID: entity.NewID(), } - manager.EXPECT().DeleteBook(b.ID).Return(nil) - handler := deleteBook(manager) + service.EXPECT().DeleteBook(b.ID).Return(nil) + handler := deleteBook(service) req, _ := http.NewRequest("DELETE", "/v1/bookmark/"+b.ID.String(), nil) r.Handle("/v1/bookmark/{id}", handler).Methods("DELETE", "OPTIONS") rr := httptest.NewRecorder() diff --git a/api/handler/loan/loan.go b/api/handler/loan.go similarity index 94% rename from api/handler/loan/loan.go rename to api/handler/loan.go index a621b9c..8e655a3 100644 --- a/api/handler/loan/loan.go +++ b/api/handler/loan.go @@ -1,4 +1,4 @@ -package loan +package handler import ( "fmt" @@ -96,8 +96,8 @@ func returnBook(bookService book.UseCase, loanService loan.UseCase) http.Handler }) } -//MakeHandlers make url handlers -func MakeHandlers(r *mux.Router, n negroni.Negroni, bookService book.UseCase, userService user.UseCase, loanService loan.UseCase) { +//MakeLoanHandlers make url handlers +func MakeLoanHandlers(r *mux.Router, n negroni.Negroni, bookService book.UseCase, userService user.UseCase, loanService loan.UseCase) { r.Handle("/v1/loan/borrow/{book_id}/{user_id}", n.With( negroni.Wrap(borrowBook(bookService, userService, loanService)), )).Methods("GET", "OPTIONS").Name("borrowBook") diff --git a/api/handler/loan/loan_test.go b/api/handler/loan_test.go similarity index 97% rename from api/handler/loan/loan_test.go rename to api/handler/loan_test.go index 881d2b9..a790ea0 100644 --- a/api/handler/loan/loan_test.go +++ b/api/handler/loan_test.go @@ -1,4 +1,4 @@ -package loan +package handler import ( "fmt" @@ -26,7 +26,7 @@ func Test_borrowBook(t *testing.T) { lMock := lmock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeHandlers(r, *n, bMock, uMock, lMock) + MakeLoanHandlers(r, *n, bMock, uMock, lMock) path, err := r.GetRoute("borrowBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/loan/borrow/{book_id}/{user_id}", path) @@ -81,7 +81,7 @@ func Test_returnBook(t *testing.T) { lMock := lmock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeHandlers(r, *n, bMock, uMock, lMock) + MakeLoanHandlers(r, *n, bMock, uMock, lMock) path, err := r.GetRoute("returnBook").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/loan/return/{book_id}", path) diff --git a/api/handler/user/user.go b/api/handler/user.go similarity index 97% rename from api/handler/user/user.go rename to api/handler/user.go index 1fb5d2c..2ecb917 100644 --- a/api/handler/user/user.go +++ b/api/handler/user.go @@ -1,4 +1,4 @@ -package user +package handler import ( "encoding/json" @@ -151,8 +151,8 @@ func deleteUser(service user.UseCase) http.Handler { }) } -//MakeHandlers make url handlers -func MakeHandlers(r *mux.Router, n negroni.Negroni, service user.UseCase) { +//MakeUserHandlers make url handlers +func MakeUserHandlers(r *mux.Router, n negroni.Negroni, service user.UseCase) { r.Handle("/v1/user", n.With( negroni.Wrap(listUsers(service)), )).Methods("GET", "OPTIONS").Name("listUsers") diff --git a/api/handler/user/user_test.go b/api/handler/user_test.go similarity index 97% rename from api/handler/user/user_test.go rename to api/handler/user_test.go index 0e39c4c..7bb6a61 100644 --- a/api/handler/user/user_test.go +++ b/api/handler/user_test.go @@ -1,4 +1,4 @@ -package user +package handler import ( "encoding/json" @@ -24,7 +24,7 @@ func Test_listUsers(t *testing.T) { m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeHandlers(r, *n, m) + MakeUserHandlers(r, *n, m) path, err := r.GetRoute("listUsers").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user", path) @@ -78,7 +78,7 @@ func Test_createUser(t *testing.T) { m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeHandlers(r, *n, m) + MakeUserHandlers(r, *n, m) path, err := r.GetRoute("createUser").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user", path) @@ -111,7 +111,7 @@ func Test_getUser(t *testing.T) { m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeHandlers(r, *n, m) + MakeUserHandlers(r, *n, m) path, err := r.GetRoute("getUser").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user/{id}", path) @@ -140,7 +140,7 @@ func Test_deleteUser(t *testing.T) { m := mock.NewMockUseCase(controller) r := mux.NewRouter() n := negroni.New() - MakeHandlers(r, *n, m) + MakeUserHandlers(r, *n, m) path, err := r.GetRoute("deleteUser").GetPathTemplate() assert.Nil(t, err) assert.Equal(t, "/v1/user/{id}", path) diff --git a/api/main.go b/api/main.go index d76195c..8793b73 100644 --- a/api/main.go +++ b/api/main.go @@ -19,9 +19,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/codegangsta/negroni" - bhandler "github.com/eminetto/clean-architecture-go-v2/api/handler/book" - lhandler "github.com/eminetto/clean-architecture-go-v2/api/handler/loan" - uhandler "github.com/eminetto/clean-architecture-go-v2/api/handler/user" + "github.com/eminetto/clean-architecture-go-v2/api/handler" "github.com/eminetto/clean-architecture-go-v2/api/middleware" "github.com/eminetto/clean-architecture-go-v2/config" "github.com/eminetto/clean-architecture-go-v2/pkg/metric" @@ -59,13 +57,13 @@ func main() { negroni.NewLogger(), ) //book - bhandler.MakeHandlers(r, *n, bookService) + handler.MakeBookHandlers(r, *n, bookService) //user - uhandler.MakeHandlers(r, *n, userService) + handler.MakeUserHandlers(r, *n, userService) //loan - lhandler.MakeHandlers(r, *n, bookService, userService, loanUseCase) + handler.MakeLoanHandlers(r, *n, bookService, userService, loanUseCase) http.Handle("/", r) http.Handle("/metrics", promhttp.Handler()) From 2c0f46aff1e41471251e3f3af0b8b9234658acc1 Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Mon, 12 Oct 2020 06:58:58 -0300 Subject: [PATCH 09/12] feat: improve repository structure --- api/main.go | 7 +++--- cmd/main.go | 6 ++--- domain/usecase/book/service_test.go | 11 ++++----- domain/usecase/user/service_test.go | 10 ++++---- .../repository_inmem.go => book_inmem.go} | 24 +++++++++---------- .../repository_mysql.go => book_mysql.go} | 24 +++++++++---------- .../repository_inmem.go => user_inmem.go} | 24 +++++++++---------- .../repository_mysql.go => user_mysql.go} | 24 +++++++++---------- 8 files changed, 64 insertions(+), 66 deletions(-) rename infra/repository/{book/repository_inmem.go => book_inmem.go} (64%) rename infra/repository/{book/repository_mysql.go => book_mysql.go} (81%) rename infra/repository/{user/repository_inmem.go => user_inmem.go} (66%) rename infra/repository/{user/repository_mysql.go => user_mysql.go} (85%) diff --git a/api/main.go b/api/main.go index 8793b73..0dd3e13 100644 --- a/api/main.go +++ b/api/main.go @@ -11,8 +11,7 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user" - brepo "github.com/eminetto/clean-architecture-go-v2/infra/repository/book" - urepo "github.com/eminetto/clean-architecture-go-v2/infra/repository/user" + "github.com/eminetto/clean-architecture-go-v2/infra/repository" "github.com/eminetto/clean-architecture-go-v2/domain/usecase/loan" @@ -37,10 +36,10 @@ func main() { } defer db.Close() - bookRepo := brepo.NewMySQLRepository(db) + bookRepo := repository.NewBookMySQL(db) bookService := book.NewService(bookRepo) - userRepo := urepo.NewMySQLRepoRepository(db) + userRepo := repository.NewUserMySQL(db) userService := user.NewService(userRepo) loanUseCase := loan.NewService(userService, bookService) diff --git a/cmd/main.go b/cmd/main.go index eabc3d9..33cfc28 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,8 +7,8 @@ import ( "log" "os" - book "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" - repo "github.com/eminetto/clean-architecture-go-v2/infra/repository/book" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" + "github.com/eminetto/clean-architecture-go-v2/infra/repository" "github.com/eminetto/clean-architecture-go-v2/config" _ "github.com/go-sql-driver/mysql" @@ -41,7 +41,7 @@ func main() { log.Fatal(err.Error()) } defer db.Close() - repo := repo.NewMySQLRepository(db) + repo := repository.NewBookMySQL(db) service := book.NewService(repo) all, err := service.SearchBooks(query) if err != nil { diff --git a/domain/usecase/book/service_test.go b/domain/usecase/book/service_test.go index 9db7d39..3c86972 100644 --- a/domain/usecase/book/service_test.go +++ b/domain/usecase/book/service_test.go @@ -5,8 +5,7 @@ import ( "time" "github.com/eminetto/clean-architecture-go-v2/domain/entity" - - "github.com/eminetto/clean-architecture-go-v2/infra/repository/book" + "github.com/eminetto/clean-architecture-go-v2/infra/repository" "github.com/eminetto/clean-architecture-go-v2/domain" @@ -24,7 +23,7 @@ func newFixtureBook() *entity.Book { } func Test_Create(t *testing.T) { - repo := book.NewInmemRepository() + repo := repository.NewBookInmem() m := NewService(repo) u := newFixtureBook() _, err := m.CreateBook(u.Title, u.Author, u.Pages, u.Quantity) @@ -33,7 +32,7 @@ func Test_Create(t *testing.T) { } func Test_SearchAndFind(t *testing.T) { - repo := book.NewInmemRepository() + repo := repository.NewBookInmem() m := NewService(repo) u1 := newFixtureBook() u2 := newFixtureBook() @@ -66,7 +65,7 @@ func Test_SearchAndFind(t *testing.T) { } func Test_Update(t *testing.T) { - repo := book.NewInmemRepository() + repo := repository.NewBookInmem() m := NewService(repo) u := newFixtureBook() id, err := m.CreateBook(u.Title, u.Author, u.Pages, u.Quantity) @@ -80,7 +79,7 @@ func Test_Update(t *testing.T) { } func TestDelete(t *testing.T) { - repo := book.NewInmemRepository() + repo := repository.NewBookInmem() m := NewService(repo) u1 := newFixtureBook() u2 := newFixtureBook() diff --git a/domain/usecase/user/service_test.go b/domain/usecase/user/service_test.go index c41b42c..a46dfda 100644 --- a/domain/usecase/user/service_test.go +++ b/domain/usecase/user/service_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/eminetto/clean-architecture-go-v2/infra/repository/user" + "github.com/eminetto/clean-architecture-go-v2/infra/repository" "github.com/eminetto/clean-architecture-go-v2/domain" "github.com/eminetto/clean-architecture-go-v2/domain/entity" @@ -24,7 +24,7 @@ func newFixtureUser() *entity.User { } func Test_Create(t *testing.T) { - repo := user.NewInmemRepository() + repo := repository.NewUserInmem() m := NewService(repo) u := newFixtureUser() _, err := m.CreateUser(u.Email, u.Password, u.FirstName, u.LastName) @@ -34,7 +34,7 @@ func Test_Create(t *testing.T) { } func Test_SearchAndFind(t *testing.T) { - repo := user.NewInmemRepository() + repo := repository.NewUserInmem() m := NewService(repo) u1 := newFixtureUser() u2 := newFixtureUser() @@ -67,7 +67,7 @@ func Test_SearchAndFind(t *testing.T) { } func Test_Update(t *testing.T) { - repo := user.NewInmemRepository() + repo := repository.NewUserInmem() m := NewService(repo) u := newFixtureUser() id, err := m.CreateUser(u.Email, u.Password, u.FirstName, u.LastName) @@ -84,7 +84,7 @@ func Test_Update(t *testing.T) { } func TestDelete(t *testing.T) { - repo := user.NewInmemRepository() + repo := repository.NewUserInmem() m := NewService(repo) u1 := newFixtureUser() u2 := newFixtureUser() diff --git a/infra/repository/book/repository_inmem.go b/infra/repository/book_inmem.go similarity index 64% rename from infra/repository/book/repository_inmem.go rename to infra/repository/book_inmem.go index 7714bf2..f3ad31c 100644 --- a/infra/repository/book/repository_inmem.go +++ b/infra/repository/book_inmem.go @@ -1,4 +1,4 @@ -package book +package repository import ( "strings" @@ -7,27 +7,27 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) -//InmemRepo in memory repo -type InmemRepo struct { +//BookInmem in memory repo +type BookInmem struct { m map[entity.ID]*entity.Book } -//NewInmemRepository create new repository -func NewInmemRepository() *InmemRepo { +//NewBookInmem create new repository +func NewBookInmem() *BookInmem { var m = map[entity.ID]*entity.Book{} - return &InmemRepo{ + return &BookInmem{ m: m, } } //Create a book -func (r *InmemRepo) Create(e *entity.Book) (entity.ID, error) { +func (r *BookInmem) Create(e *entity.Book) (entity.ID, error) { r.m[e.ID] = e return e.ID, nil } //Get a book -func (r *InmemRepo) Get(id entity.ID) (*entity.Book, error) { +func (r *BookInmem) Get(id entity.ID) (*entity.Book, error) { if r.m[id] == nil { // return nil, fmt.Errorf("not found") return nil, domain.ErrNotFound @@ -36,7 +36,7 @@ func (r *InmemRepo) Get(id entity.ID) (*entity.Book, error) { } //Update a book -func (r *InmemRepo) Update(e *entity.Book) error { +func (r *BookInmem) Update(e *entity.Book) error { _, err := r.Get(e.ID) if err != nil { return err @@ -46,7 +46,7 @@ func (r *InmemRepo) Update(e *entity.Book) error { } //Search books -func (r *InmemRepo) Search(query string) ([]*entity.Book, error) { +func (r *BookInmem) Search(query string) ([]*entity.Book, error) { var d []*entity.Book for _, j := range r.m { if strings.Contains(strings.ToLower(j.Title), query) { @@ -57,7 +57,7 @@ func (r *InmemRepo) Search(query string) ([]*entity.Book, error) { } //List books -func (r *InmemRepo) List() ([]*entity.Book, error) { +func (r *BookInmem) List() ([]*entity.Book, error) { var d []*entity.Book for _, j := range r.m { d = append(d, j) @@ -66,7 +66,7 @@ func (r *InmemRepo) List() ([]*entity.Book, error) { } //Delete a book -func (r *InmemRepo) Delete(id entity.ID) error { +func (r *BookInmem) Delete(id entity.ID) error { if r.m[id] == nil { // return fmt.Errorf("not found") return domain.ErrNotFound diff --git a/infra/repository/book/repository_mysql.go b/infra/repository/book_mysql.go similarity index 81% rename from infra/repository/book/repository_mysql.go rename to infra/repository/book_mysql.go index f7945cf..607fc11 100644 --- a/infra/repository/book/repository_mysql.go +++ b/infra/repository/book_mysql.go @@ -1,4 +1,4 @@ -package book +package repository import ( "database/sql" @@ -7,20 +7,20 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) -//MySQLRepo mysql repo -type MySQLRepo struct { +//BookMySQL mysql repo +type BookMySQL struct { db *sql.DB } -//NewMySQLRepository create new repository -func NewMySQLRepository(db *sql.DB) *MySQLRepo { - return &MySQLRepo{ +//NewBookMySQL create new repository +func NewBookMySQL(db *sql.DB) *BookMySQL { + return &BookMySQL{ db: db, } } //Create a book -func (r *MySQLRepo) Create(e *entity.Book) (entity.ID, error) { +func (r *BookMySQL) Create(e *entity.Book) (entity.ID, error) { stmt, err := r.db.Prepare(` insert into book (id, title, author, pages, quantity, created_at) values(?,?,?,?,?,?)`) @@ -46,7 +46,7 @@ func (r *MySQLRepo) Create(e *entity.Book) (entity.ID, error) { } //Get a book -func (r *MySQLRepo) Get(id entity.ID) (*entity.Book, error) { +func (r *BookMySQL) Get(id entity.ID) (*entity.Book, error) { stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book where id = ?`) if err != nil { return nil, err @@ -63,7 +63,7 @@ func (r *MySQLRepo) Get(id entity.ID) (*entity.Book, error) { } //Update a book -func (r *MySQLRepo) Update(e *entity.Book) error { +func (r *BookMySQL) Update(e *entity.Book) error { e.UpdatedAt = time.Now() _, err := r.db.Exec("update book set title = ?, author = ?, pages = ?, quantity = ?, updated_at = ? where id = ?", e.Title, e.Author, e.Pages, e.Quantity, e.UpdatedAt.Format("2006-01-02"), e.ID) if err != nil { @@ -73,7 +73,7 @@ func (r *MySQLRepo) Update(e *entity.Book) error { } //Search books -func (r *MySQLRepo) Search(query string) ([]*entity.Book, error) { +func (r *BookMySQL) Search(query string) ([]*entity.Book, error) { stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book where title like ?`) if err != nil { return nil, err @@ -96,7 +96,7 @@ func (r *MySQLRepo) Search(query string) ([]*entity.Book, error) { } //List books -func (r *MySQLRepo) List() ([]*entity.Book, error) { +func (r *BookMySQL) List() ([]*entity.Book, error) { stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book`) if err != nil { return nil, err @@ -118,7 +118,7 @@ func (r *MySQLRepo) List() ([]*entity.Book, error) { } //Delete a book -func (r *MySQLRepo) Delete(id entity.ID) error { +func (r *BookMySQL) Delete(id entity.ID) error { _, err := r.db.Exec("delete from book where id = ?", id) if err != nil { return err diff --git a/infra/repository/user/repository_inmem.go b/infra/repository/user_inmem.go similarity index 66% rename from infra/repository/user/repository_inmem.go rename to infra/repository/user_inmem.go index eb746bf..b54d1fb 100644 --- a/infra/repository/user/repository_inmem.go +++ b/infra/repository/user_inmem.go @@ -1,4 +1,4 @@ -package user +package repository import ( "fmt" @@ -8,27 +8,27 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) -//IRepo in memory repo -type IRepo struct { +//UserInmem in memory repo +type UserInmem struct { m map[entity.ID]*entity.User } -//NewInmemRepository create new repository -func NewInmemRepository() *IRepo { +//NewUserInmem create new repository +func NewUserInmem() *UserInmem { var m = map[entity.ID]*entity.User{} - return &IRepo{ + return &UserInmem{ m: m, } } //Create an user -func (r *IRepo) Create(e *entity.User) (entity.ID, error) { +func (r *UserInmem) Create(e *entity.User) (entity.ID, error) { r.m[e.ID] = e return e.ID, nil } //Get an user -func (r *IRepo) Get(id entity.ID) (*entity.User, error) { +func (r *UserInmem) Get(id entity.ID) (*entity.User, error) { if r.m[id] == nil { // return nil, fmt.Errorf("not found") return nil, domain.ErrNotFound @@ -37,7 +37,7 @@ func (r *IRepo) Get(id entity.ID) (*entity.User, error) { } //Update an user -func (r *IRepo) Update(e *entity.User) error { +func (r *UserInmem) Update(e *entity.User) error { _, err := r.Get(e.ID) if err != nil { return err @@ -47,7 +47,7 @@ func (r *IRepo) Update(e *entity.User) error { } //Search users -func (r *IRepo) Search(query string) ([]*entity.User, error) { +func (r *UserInmem) Search(query string) ([]*entity.User, error) { var d []*entity.User for _, j := range r.m { if strings.Contains(strings.ToLower(j.FirstName), query) { @@ -63,7 +63,7 @@ func (r *IRepo) Search(query string) ([]*entity.User, error) { } //List users -func (r *IRepo) List() ([]*entity.User, error) { +func (r *UserInmem) List() ([]*entity.User, error) { var d []*entity.User for _, j := range r.m { d = append(d, j) @@ -72,7 +72,7 @@ func (r *IRepo) List() ([]*entity.User, error) { } //Delete an user -func (r *IRepo) Delete(id entity.ID) error { +func (r *UserInmem) Delete(id entity.ID) error { if r.m[id] == nil { return fmt.Errorf("not found") } diff --git a/infra/repository/user/repository_mysql.go b/infra/repository/user_mysql.go similarity index 85% rename from infra/repository/user/repository_mysql.go rename to infra/repository/user_mysql.go index 41882fc..738e853 100644 --- a/infra/repository/user/repository_mysql.go +++ b/infra/repository/user_mysql.go @@ -1,4 +1,4 @@ -package user +package repository import ( "database/sql" @@ -8,20 +8,20 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) -//MySQLRepo mysql repo -type MySQLRepo struct { +//UserMySQL mysql repo +type UserMySQL struct { db *sql.DB } -//NewMySQLRepoRepository create new repository -func NewMySQLRepoRepository(db *sql.DB) *MySQLRepo { - return &MySQLRepo{ +//NewUserMySQL create new repository +func NewUserMySQL(db *sql.DB) *UserMySQL { + return &UserMySQL{ db: db, } } //Create an user -func (r *MySQLRepo) Create(e *entity.User) (entity.ID, error) { +func (r *UserMySQL) Create(e *entity.User) (entity.ID, error) { stmt, err := r.db.Prepare(` insert into user (id, email, password, first_name, last_name, created_at) values(?,?,?,?,?,?)`) @@ -47,7 +47,7 @@ func (r *MySQLRepo) Create(e *entity.User) (entity.ID, error) { } //Get an user -func (r *MySQLRepo) Get(id entity.ID) (*entity.User, error) { +func (r *UserMySQL) Get(id entity.ID) (*entity.User, error) { return getUser(id, r.db) } @@ -81,7 +81,7 @@ func getUser(id entity.ID, db *sql.DB) (*entity.User, error) { } //Update an user -func (r *MySQLRepo) Update(e *entity.User) error { +func (r *UserMySQL) Update(e *entity.User) error { e.UpdatedAt = time.Now() _, err := r.db.Exec("update user set email = ?, password = ?, first_name = ?, last_name = ?, updated_at = ? where id = ?", e.Email, e.Password, e.FirstName, e.LastName, e.UpdatedAt.Format("2006-01-02"), e.ID) if err != nil { @@ -101,7 +101,7 @@ func (r *MySQLRepo) Update(e *entity.User) error { } //Search users -func (r *MySQLRepo) Search(query string) ([]*entity.User, error) { +func (r *UserMySQL) Search(query string) ([]*entity.User, error) { stmt, err := r.db.Prepare(`select id from user where name like ?`) if err != nil { return nil, err @@ -135,7 +135,7 @@ func (r *MySQLRepo) Search(query string) ([]*entity.User, error) { } //List users -func (r *MySQLRepo) List() ([]*entity.User, error) { +func (r *UserMySQL) List() ([]*entity.User, error) { stmt, err := r.db.Prepare(`select id from user`) if err != nil { return nil, err @@ -169,7 +169,7 @@ func (r *MySQLRepo) List() ([]*entity.User, error) { } //Delete an user -func (r *MySQLRepo) Delete(id entity.ID) error { +func (r *UserMySQL) Delete(id entity.ID) error { _, err := r.db.Exec("delete from user where id = ?", id) if err != nil { return err From 3e4eb2e6161dc961dc4a1fe230e3798c446e6dd1 Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Mon, 12 Oct 2020 07:35:29 -0300 Subject: [PATCH 10/12] feat: move entity and usecase to root folder --- Makefile | 6 ++--- api/handler/book.go | 10 +++----- api/handler/book_test.go | 7 +++--- api/handler/loan.go | 18 ++++++------- api/handler/loan_test.go | 15 ++++++----- api/handler/user.go | 10 +++----- api/handler/user_test.go | 7 +++--- api/main.go | 6 ++--- api/presenter/book.go | 2 +- api/presenter/user.go | 2 +- cmd/main.go | 2 +- {domain/entity => entity}/book.go | 6 ++--- {domain/entity => entity}/book_test.go | 11 ++++---- {domain/entity => entity}/entity.go | 0 {domain => entity}/error.go | 2 +- {domain/entity => entity}/user.go | 11 ++++---- {domain/entity => entity}/user_test.go | 15 ++++++----- infra/repository/book_inmem.go | 9 +++---- infra/repository/book_mysql.go | 2 +- infra/repository/user_inmem.go | 9 +++---- infra/repository/user_mysql.go | 2 +- {domain/usecase => usecase}/book/interface.go | 2 +- {domain/usecase => usecase}/book/mock/book.go | 4 +-- {domain/usecase => usecase}/book/service.go | 10 +++----- .../usecase => usecase}/book/service_test.go | 10 +++----- {domain/usecase => usecase}/loan/interface.go | 2 +- {domain/usecase => usecase}/loan/mock/loan.go | 4 +-- {domain/usecase => usecase}/loan/service.go | 11 ++++---- .../usecase => usecase}/loan/service_test.go | 25 +++++++++---------- {domain/usecase => usecase}/user/interface.go | 2 +- {domain/usecase => usecase}/user/mock/user.go | 4 +-- {domain/usecase => usecase}/user/service.go | 10 +++----- .../usecase => usecase}/user/service_test.go | 11 ++++---- 33 files changed, 109 insertions(+), 138 deletions(-) rename {domain/entity => entity}/book.go (83%) rename {domain/entity => entity}/book_test.go (79%) rename {domain/entity => entity}/entity.go (100%) rename {domain => entity}/error.go (97%) rename {domain/entity => entity}/user.go (87%) rename {domain/entity => entity}/user_test.go (85%) rename {domain/usecase => usecase}/book/interface.go (92%) rename {domain/usecase => usecase}/book/mock/book.go (99%) rename {domain/usecase => usecase}/book/service.go (86%) rename {domain/usecase => usecase}/book/service_test.go (89%) rename {domain/usecase => usecase}/loan/interface.go (71%) rename {domain/usecase => usecase}/loan/mock/loan.go (93%) rename {domain/usecase => usecase}/loan/service.go (82%) rename {domain/usecase => usecase}/loan/service_test.go (78%) rename {domain/usecase => usecase}/user/interface.go (92%) rename {domain/usecase => usecase}/user/mock/user.go (99%) rename {domain/usecase => usecase}/user/service.go (85%) rename {domain/usecase => usecase}/user/service_test.go (89%) diff --git a/Makefile b/Makefile index 025583f..6320955 100644 --- a/Makefile +++ b/Makefile @@ -32,9 +32,9 @@ ci: dependencies test build-mocks: @go get github.com/golang/mock/gomock @go install github.com/golang/mock/mockgen - @~/go/bin/mockgen -source=domain/usecase/book/interface.go -destination=domain/usecase/book/mock/book.go -package=mock - @~/go/bin/mockgen -source=domain/usecase/user/interface.go -destination=domain/usecase/user/mock/user.go -package=mock - @~/go/bin/mockgen -source=domain/usecase/loan/interface.go -destination=domain/usecase/loan/mock/loan.go -package=mock + @~/go/bin/mockgen -source=usecase/book/interface.go -destination=usecase/book/mock/book.go -package=mock + @~/go/bin/mockgen -source=usecase/user/interface.go -destination=usecase/user/mock/user.go -package=mock + @~/go/bin/mockgen -source=usecase/loan/interface.go -destination=usecase/loan/mock/loan.go -package=mock test: go test -tags testing ./... diff --git a/api/handler/book.go b/api/handler/book.go index 65be03d..f6dc6f8 100644 --- a/api/handler/book.go +++ b/api/handler/book.go @@ -5,14 +5,12 @@ import ( "log" "net/http" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" - - "github.com/eminetto/clean-architecture-go-v2/domain" + "github.com/eminetto/clean-architecture-go-v2/usecase/book" "github.com/eminetto/clean-architecture-go-v2/api/presenter" "github.com/codegangsta/negroni" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" "github.com/gorilla/mux" ) @@ -29,7 +27,7 @@ func listBooks(service book.UseCase) http.Handler { data, err = service.SearchBooks(title) } w.Header().Set("Content-Type", "application/json") - if err != nil && err != domain.ErrNotFound { + if err != nil && err != entity.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return @@ -108,7 +106,7 @@ func getBook(service book.UseCase) http.Handler { return } data, err := service.GetBook(id) - if err != nil && err != domain.ErrNotFound { + if err != nil && err != entity.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return diff --git a/api/handler/book_test.go b/api/handler/book_test.go index c6103db..cf2c6b5 100644 --- a/api/handler/book_test.go +++ b/api/handler/book_test.go @@ -8,11 +8,10 @@ import ( "strings" "testing" - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" "github.com/codegangsta/negroni" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book/mock" + "github.com/eminetto/clean-architecture-go-v2/usecase/book/mock" "github.com/golang/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" @@ -49,7 +48,7 @@ func Test_listBooks_NotFound(t *testing.T) { defer ts.Close() service.EXPECT(). SearchBooks("book of books"). - Return(nil, domain.ErrNotFound) + Return(nil, entity.ErrNotFound) res, err := http.Get(ts.URL + "?title=book+of+books") assert.Nil(t, err) assert.Equal(t, http.StatusNotFound, res.StatusCode) diff --git a/api/handler/loan.go b/api/handler/loan.go index 8e655a3..16ea7e0 100644 --- a/api/handler/loan.go +++ b/api/handler/loan.go @@ -4,15 +4,13 @@ import ( "fmt" "net/http" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user" + "github.com/eminetto/clean-architecture-go-v2/usecase/book" + "github.com/eminetto/clean-architecture-go-v2/usecase/user" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/loan" - - "github.com/eminetto/clean-architecture-go-v2/domain" + "github.com/eminetto/clean-architecture-go-v2/usecase/loan" "github.com/codegangsta/negroni" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" "github.com/gorilla/mux" ) @@ -27,7 +25,7 @@ func borrowBook(bookService book.UseCase, userService user.UseCase, loanService return } b, err := bookService.GetBook(bID) - if err != nil && err != domain.ErrNotFound { + if err != nil && err != entity.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return @@ -44,7 +42,7 @@ func borrowBook(bookService book.UseCase, userService user.UseCase, loanService return } u, err := userService.GetUser(uID) - if err != nil && err != domain.ErrNotFound { + if err != nil && err != entity.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return @@ -76,7 +74,7 @@ func returnBook(bookService book.UseCase, loanService loan.UseCase) http.Handler return } b, err := bookService.GetBook(bID) - if err != nil && err != domain.ErrNotFound { + if err != nil && err != entity.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return @@ -87,7 +85,7 @@ func returnBook(bookService book.UseCase, loanService loan.UseCase) http.Handler return } err = loanService.Return(b) - if err != nil && err != domain.ErrNotFound { + if err != nil && err != entity.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return diff --git a/api/handler/loan_test.go b/api/handler/loan_test.go index a790ea0..7131c9c 100644 --- a/api/handler/loan_test.go +++ b/api/handler/loan_test.go @@ -6,13 +6,12 @@ import ( "net/http/httptest" "testing" - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" "github.com/codegangsta/negroni" - bmock "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book/mock" - lmock "github.com/eminetto/clean-architecture-go-v2/domain/usecase/loan/mock" - umock "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user/mock" + bmock "github.com/eminetto/clean-architecture-go-v2/usecase/book/mock" + lmock "github.com/eminetto/clean-architecture-go-v2/usecase/loan/mock" + umock "github.com/eminetto/clean-architecture-go-v2/usecase/user/mock" "github.com/golang/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" @@ -35,7 +34,7 @@ func Test_borrowBook(t *testing.T) { t.Run("book not found", func(t *testing.T) { bID := entity.NewID() uID := entity.NewID() - bMock.EXPECT().GetBook(bID).Return(nil, domain.ErrNotFound) + bMock.EXPECT().GetBook(bID).Return(nil, entity.ErrNotFound) ts := httptest.NewServer(r) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/v1/loan/borrow/%s/%s", ts.URL, bID.String(), uID.String())) @@ -48,7 +47,7 @@ func Test_borrowBook(t *testing.T) { } uID := entity.NewID() bMock.EXPECT().GetBook(b.ID).Return(b, nil) - uMock.EXPECT().GetUser(uID).Return(nil, domain.ErrNotFound) + uMock.EXPECT().GetUser(uID).Return(nil, entity.ErrNotFound) ts := httptest.NewServer(r) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/v1/loan/borrow/%s/%s", ts.URL, b.ID.String(), uID.String())) @@ -89,7 +88,7 @@ func Test_returnBook(t *testing.T) { r.Handle("/v1/loan/return/{book_id}", handler) t.Run("book not found", func(t *testing.T) { bID := entity.NewID() - bMock.EXPECT().GetBook(bID).Return(nil, domain.ErrNotFound) + bMock.EXPECT().GetBook(bID).Return(nil, entity.ErrNotFound) ts := httptest.NewServer(r) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/v1/loan/return/%s", ts.URL, bID.String())) diff --git a/api/handler/user.go b/api/handler/user.go index 2ecb917..4b1b45f 100644 --- a/api/handler/user.go +++ b/api/handler/user.go @@ -5,13 +5,11 @@ import ( "log" "net/http" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user" - - "github.com/eminetto/clean-architecture-go-v2/domain" + "github.com/eminetto/clean-architecture-go-v2/usecase/user" "github.com/eminetto/clean-architecture-go-v2/api/presenter" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" "github.com/codegangsta/negroni" "github.com/gorilla/mux" @@ -30,7 +28,7 @@ func listUsers(service user.UseCase) http.Handler { data, err = service.SearchUsers(name) } w.Header().Set("Content-Type", "application/json") - if err != nil && err != domain.ErrNotFound { + if err != nil && err != entity.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return @@ -108,7 +106,7 @@ func getUser(service user.UseCase) http.Handler { } data, err := service.GetUser(id) w.Header().Set("Content-Type", "application/json") - if err != nil && err != domain.ErrNotFound { + if err != nil && err != entity.ErrNotFound { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return diff --git a/api/handler/user_test.go b/api/handler/user_test.go index 7bb6a61..05b06de 100644 --- a/api/handler/user_test.go +++ b/api/handler/user_test.go @@ -10,9 +10,8 @@ import ( "github.com/codegangsta/negroni" "github.com/eminetto/clean-architecture-go-v2/api/presenter" - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user/mock" + "github.com/eminetto/clean-architecture-go-v2/entity" + "github.com/eminetto/clean-architecture-go-v2/usecase/user/mock" "github.com/golang/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" @@ -49,7 +48,7 @@ func Test_listUsers_NotFound(t *testing.T) { defer ts.Close() m.EXPECT(). SearchUsers("dio"). - Return(nil, domain.ErrNotFound) + Return(nil, entity.ErrNotFound) res, err := http.Get(ts.URL + "?name=dio") assert.Nil(t, err) assert.Equal(t, http.StatusNotFound, res.StatusCode) diff --git a/api/main.go b/api/main.go index 0dd3e13..3c49d9c 100644 --- a/api/main.go +++ b/api/main.go @@ -9,11 +9,11 @@ import ( "strconv" "time" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user" "github.com/eminetto/clean-architecture-go-v2/infra/repository" + "github.com/eminetto/clean-architecture-go-v2/usecase/book" + "github.com/eminetto/clean-architecture-go-v2/usecase/user" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/loan" + "github.com/eminetto/clean-architecture-go-v2/usecase/loan" "github.com/prometheus/client_golang/prometheus/promhttp" diff --git a/api/presenter/book.go b/api/presenter/book.go index 257c7e5..ba52eb3 100644 --- a/api/presenter/book.go +++ b/api/presenter/book.go @@ -1,7 +1,7 @@ package presenter import ( - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" ) //Book data diff --git a/api/presenter/user.go b/api/presenter/user.go index 05dfb3e..bf4b97e 100644 --- a/api/presenter/user.go +++ b/api/presenter/user.go @@ -1,7 +1,7 @@ package presenter import ( - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" ) //User data diff --git a/cmd/main.go b/cmd/main.go index 33cfc28..dfa9c23 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,8 +7,8 @@ import ( "log" "os" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" "github.com/eminetto/clean-architecture-go-v2/infra/repository" + "github.com/eminetto/clean-architecture-go-v2/usecase/book" "github.com/eminetto/clean-architecture-go-v2/config" _ "github.com/go-sql-driver/mysql" diff --git a/domain/entity/book.go b/entity/book.go similarity index 83% rename from domain/entity/book.go rename to entity/book.go index b1e0502..87b9c24 100644 --- a/domain/entity/book.go +++ b/entity/book.go @@ -2,8 +2,6 @@ package entity import ( "time" - - "github.com/eminetto/clean-architecture-go-v2/domain" ) //Book data @@ -29,7 +27,7 @@ func NewBook(title string, author string, pages int, quantity int) (*Book, error } err := b.Validate() if err != nil { - return nil, domain.ErrInvalidEntity + return nil, ErrInvalidEntity } return b, nil } @@ -37,7 +35,7 @@ func NewBook(title string, author string, pages int, quantity int) (*Book, error //Validate validate book func (b *Book) Validate() error { if b.Title == "" || b.Author == "" || b.Pages <= 0 || b.Quantity <= 0 { - return domain.ErrInvalidEntity + return ErrInvalidEntity } return nil } diff --git a/domain/entity/book_test.go b/entity/book_test.go similarity index 79% rename from domain/entity/book_test.go rename to entity/book_test.go index a383e98..a77a33d 100644 --- a/domain/entity/book_test.go +++ b/entity/book_test.go @@ -3,8 +3,7 @@ package entity_test import ( "testing" - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" "github.com/stretchr/testify/assert" ) @@ -37,28 +36,28 @@ func TestBookValidate(t *testing.T) { author: "Neil Gaiman", pages: 100, quantity: 0, - want: domain.ErrInvalidEntity, + want: entity.ErrInvalidEntity, }, { title: "", author: "Neil Gaiman", pages: 100, quantity: 1, - want: domain.ErrInvalidEntity, + want: entity.ErrInvalidEntity, }, { title: "American Gods", author: "", pages: 100, quantity: 1, - want: domain.ErrInvalidEntity, + want: entity.ErrInvalidEntity, }, { title: "American Gods", author: "Neil Gaiman", pages: 0, quantity: 1, - want: domain.ErrInvalidEntity, + want: entity.ErrInvalidEntity, }, } for _, tc := range tests { diff --git a/domain/entity/entity.go b/entity/entity.go similarity index 100% rename from domain/entity/entity.go rename to entity/entity.go diff --git a/domain/error.go b/entity/error.go similarity index 97% rename from domain/error.go rename to entity/error.go index c86196b..f63c9d1 100644 --- a/domain/error.go +++ b/entity/error.go @@ -1,4 +1,4 @@ -package domain +package entity import "errors" diff --git a/domain/entity/user.go b/entity/user.go similarity index 87% rename from domain/entity/user.go rename to entity/user.go index b182723..963da6a 100644 --- a/domain/entity/user.go +++ b/entity/user.go @@ -3,7 +3,6 @@ package entity import ( "time" - "github.com/eminetto/clean-architecture-go-v2/domain" "golang.org/x/crypto/bcrypt" ) @@ -35,7 +34,7 @@ func NewUser(email, password, firstName, lastName string) (*User, error) { u.Password = pwd err = u.Validate() if err != nil { - return nil, domain.ErrInvalidEntity + return nil, ErrInvalidEntity } return u, nil } @@ -44,7 +43,7 @@ func NewUser(email, password, firstName, lastName string) (*User, error) { func (u *User) AddBook(id ID) error { _, err := u.GetBook(id) if err == nil { - return domain.ErrBookAlreadyBorrowed + return ErrBookAlreadyBorrowed } u.Books = append(u.Books, id) return nil @@ -58,7 +57,7 @@ func (u *User) RemoveBook(id ID) error { return nil } } - return domain.ErrNotFound + return ErrNotFound } //GetBook get a book @@ -68,13 +67,13 @@ func (u *User) GetBook(id ID) (ID, error) { return id, nil } } - return id, domain.ErrNotFound + return id, ErrNotFound } //Validate validate data func (u *User) Validate() error { if u.Email == "" || u.FirstName == "" || u.LastName == "" || u.Password == "" { - return domain.ErrInvalidEntity + return ErrInvalidEntity } return nil diff --git a/domain/entity/user_test.go b/entity/user_test.go similarity index 85% rename from domain/entity/user_test.go rename to entity/user_test.go index 9c16439..01d4f76 100644 --- a/domain/entity/user_test.go +++ b/entity/user_test.go @@ -3,8 +3,7 @@ package entity_test import ( "testing" - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" "github.com/stretchr/testify/assert" ) @@ -32,14 +31,14 @@ func TestAddBook(t *testing.T) { assert.Nil(t, err) assert.Equal(t, 1, len(u.Books)) err = u.AddBook(bID) - assert.Equal(t, domain.ErrBookAlreadyBorrowed, err) + assert.Equal(t, entity.ErrBookAlreadyBorrowed, err) } func TestRemoveBook(t *testing.T) { u, _ := entity.NewUser("sjobs@apple.com", "new_password", "Steve", "Jobs") err := u.RemoveBook(entity.NewID()) - assert.Equal(t, domain.ErrNotFound, err) + assert.Equal(t, entity.ErrNotFound, err) bID := entity.NewID() _ = u.AddBook(bID) err = u.RemoveBook(bID) @@ -54,7 +53,7 @@ func TestGetBook(t *testing.T) { assert.Nil(t, err) assert.Equal(t, id, bID) _, err = u.GetBook(entity.NewID()) - assert.Equal(t, domain.ErrNotFound, err) + assert.Equal(t, entity.ErrNotFound, err) } func TestUserValidate(t *testing.T) { @@ -79,7 +78,7 @@ func TestUserValidate(t *testing.T) { password: "new_password", firstName: "Steve", lastName: "Jobs", - want: domain.ErrInvalidEntity, + want: entity.ErrInvalidEntity, }, { email: "sjobs@apple.com", @@ -93,14 +92,14 @@ func TestUserValidate(t *testing.T) { password: "new_password", firstName: "", lastName: "Jobs", - want: domain.ErrInvalidEntity, + want: entity.ErrInvalidEntity, }, { email: "sjobs@apple.com", password: "new_password", firstName: "Steve", lastName: "", - want: domain.ErrInvalidEntity, + want: entity.ErrInvalidEntity, }, } for _, tc := range tests { diff --git a/infra/repository/book_inmem.go b/infra/repository/book_inmem.go index f3ad31c..6dc393f 100644 --- a/infra/repository/book_inmem.go +++ b/infra/repository/book_inmem.go @@ -3,8 +3,7 @@ package repository import ( "strings" - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" ) //BookInmem in memory repo @@ -29,8 +28,7 @@ func (r *BookInmem) Create(e *entity.Book) (entity.ID, error) { //Get a book func (r *BookInmem) Get(id entity.ID) (*entity.Book, error) { if r.m[id] == nil { - // return nil, fmt.Errorf("not found") - return nil, domain.ErrNotFound + return nil, entity.ErrNotFound } return r.m[id], nil } @@ -68,8 +66,7 @@ func (r *BookInmem) List() ([]*entity.Book, error) { //Delete a book func (r *BookInmem) Delete(id entity.ID) error { if r.m[id] == nil { - // return fmt.Errorf("not found") - return domain.ErrNotFound + return entity.ErrNotFound } r.m[id] = nil return nil diff --git a/infra/repository/book_mysql.go b/infra/repository/book_mysql.go index 607fc11..f8ae076 100644 --- a/infra/repository/book_mysql.go +++ b/infra/repository/book_mysql.go @@ -4,7 +4,7 @@ import ( "database/sql" "time" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" ) //BookMySQL mysql repo diff --git a/infra/repository/user_inmem.go b/infra/repository/user_inmem.go index b54d1fb..0ffd08d 100644 --- a/infra/repository/user_inmem.go +++ b/infra/repository/user_inmem.go @@ -4,8 +4,7 @@ import ( "fmt" "strings" - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" ) //UserInmem in memory repo @@ -30,8 +29,7 @@ func (r *UserInmem) Create(e *entity.User) (entity.ID, error) { //Get an user func (r *UserInmem) Get(id entity.ID) (*entity.User, error) { if r.m[id] == nil { - // return nil, fmt.Errorf("not found") - return nil, domain.ErrNotFound + return nil, entity.ErrNotFound } return r.m[id], nil } @@ -55,8 +53,7 @@ func (r *UserInmem) Search(query string) ([]*entity.User, error) { } } if len(d) == 0 { - // return nil, fmt.Errorf("not found") - return nil, domain.ErrNotFound + return nil, entity.ErrNotFound } return d, nil diff --git a/infra/repository/user_mysql.go b/infra/repository/user_mysql.go index 738e853..73cb0bb 100644 --- a/infra/repository/user_mysql.go +++ b/infra/repository/user_mysql.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" ) //UserMySQL mysql repo diff --git a/domain/usecase/book/interface.go b/usecase/book/interface.go similarity index 92% rename from domain/usecase/book/interface.go rename to usecase/book/interface.go index 07cdb00..3be17a6 100644 --- a/domain/usecase/book/interface.go +++ b/usecase/book/interface.go @@ -1,7 +1,7 @@ package book import ( - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" ) //Reader interface diff --git a/domain/usecase/book/mock/book.go b/usecase/book/mock/book.go similarity index 99% rename from domain/usecase/book/mock/book.go rename to usecase/book/mock/book.go index 4afe5e9..86920a0 100644 --- a/domain/usecase/book/mock/book.go +++ b/usecase/book/mock/book.go @@ -1,11 +1,11 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: domain/usecase/book/interface.go +// Source: usecase/book/interface.go // Package mock is a generated GoMock package. package mock import ( - entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" + entity "github.com/eminetto/clean-architecture-go-v2/entity" gomock "github.com/golang/mock/gomock" reflect "reflect" ) diff --git a/domain/usecase/book/service.go b/usecase/book/service.go similarity index 86% rename from domain/usecase/book/service.go rename to usecase/book/service.go index fdbcaf3..a0ebb4e 100644 --- a/domain/usecase/book/service.go +++ b/usecase/book/service.go @@ -4,9 +4,7 @@ import ( "strings" "time" - "github.com/eminetto/clean-architecture-go-v2/domain" - - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" ) //Service book usecase @@ -34,7 +32,7 @@ func (s *Service) CreateBook(title string, author string, pages int, quantity in func (s *Service) GetBook(id entity.ID) (*entity.Book, error) { b, err := s.repo.Get(id) if b == nil { - return nil, domain.ErrNotFound + return nil, entity.ErrNotFound } if err != nil { return nil, err @@ -50,7 +48,7 @@ func (s *Service) SearchBooks(query string) ([]*entity.Book, error) { return nil, err } if len(books) == 0 { - return nil, domain.ErrNotFound + return nil, entity.ErrNotFound } return books, nil } @@ -62,7 +60,7 @@ func (s *Service) ListBooks() ([]*entity.Book, error) { return nil, err } if len(books) == 0 { - return nil, domain.ErrNotFound + return nil, entity.ErrNotFound } return books, nil } diff --git a/domain/usecase/book/service_test.go b/usecase/book/service_test.go similarity index 89% rename from domain/usecase/book/service_test.go rename to usecase/book/service_test.go index 3c86972..47ffbe8 100644 --- a/domain/usecase/book/service_test.go +++ b/usecase/book/service_test.go @@ -4,11 +4,9 @@ import ( "testing" "time" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" "github.com/eminetto/clean-architecture-go-v2/infra/repository" - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/stretchr/testify/assert" ) @@ -48,7 +46,7 @@ func Test_SearchAndFind(t *testing.T) { assert.Equal(t, "I Am Ozzy", c[0].Title) c, err = m.SearchBooks("dio") - assert.Equal(t, domain.ErrNotFound, err) + assert.Equal(t, entity.ErrNotFound, err) assert.Nil(t, c) }) t.Run("list all", func(t *testing.T) { @@ -86,10 +84,10 @@ func TestDelete(t *testing.T) { u2ID, _ := m.CreateBook(u2.Title, u2.Author, u2.Pages, u2.Quantity) err := m.DeleteBook(u1.ID) - assert.Equal(t, domain.ErrNotFound, err) + assert.Equal(t, entity.ErrNotFound, err) err = m.DeleteBook(u2ID) assert.Nil(t, err) _, err = m.GetBook(u2ID) - assert.Equal(t, domain.ErrNotFound, err) + assert.Equal(t, entity.ErrNotFound, err) } diff --git a/domain/usecase/loan/interface.go b/usecase/loan/interface.go similarity index 71% rename from domain/usecase/loan/interface.go rename to usecase/loan/interface.go index 6bbf546..802a6ef 100644 --- a/domain/usecase/loan/interface.go +++ b/usecase/loan/interface.go @@ -1,7 +1,7 @@ package loan import ( - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" ) //UseCase use case interface diff --git a/domain/usecase/loan/mock/loan.go b/usecase/loan/mock/loan.go similarity index 93% rename from domain/usecase/loan/mock/loan.go rename to usecase/loan/mock/loan.go index 0d25a74..d3518a0 100644 --- a/domain/usecase/loan/mock/loan.go +++ b/usecase/loan/mock/loan.go @@ -1,11 +1,11 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: domain/usecase/loan/interface.go +// Source: usecase/loan/interface.go // Package mock is a generated GoMock package. package mock import ( - entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" + entity "github.com/eminetto/clean-architecture-go-v2/entity" gomock "github.com/golang/mock/gomock" reflect "reflect" ) diff --git a/domain/usecase/loan/service.go b/usecase/loan/service.go similarity index 82% rename from domain/usecase/loan/service.go rename to usecase/loan/service.go index edc6c09..7516333 100644 --- a/domain/usecase/loan/service.go +++ b/usecase/loan/service.go @@ -1,10 +1,9 @@ package loan import ( - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book" - "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user" + "github.com/eminetto/clean-architecture-go-v2/entity" + "github.com/eminetto/clean-architecture-go-v2/usecase/book" + "github.com/eminetto/clean-architecture-go-v2/usecase/user" ) //Service loan usecase @@ -32,7 +31,7 @@ func (s *Service) Borrow(u *entity.User, b *entity.Book) error { return err } if b.Quantity <= 0 { - return domain.ErrNotEnoughBooks + return entity.ErrNotEnoughBooks } err = u.AddBook(b.ID) @@ -74,7 +73,7 @@ func (s *Service) Return(b *entity.Book) error { break } if !borrowed { - return domain.ErrBookNotBorrowed + return entity.ErrBookNotBorrowed } u, err := s.userService.GetUser(borrowedBy) if err != nil { diff --git a/domain/usecase/loan/service_test.go b/usecase/loan/service_test.go similarity index 78% rename from domain/usecase/loan/service_test.go rename to usecase/loan/service_test.go index cd68a2a..68f30fd 100644 --- a/domain/usecase/loan/service_test.go +++ b/usecase/loan/service_test.go @@ -3,11 +3,10 @@ package loan import ( "testing" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" - "github.com/eminetto/clean-architecture-go-v2/domain" - bmock "github.com/eminetto/clean-architecture-go-v2/domain/usecase/book/mock" - umock "github.com/eminetto/clean-architecture-go-v2/domain/usecase/user/mock" + bmock "github.com/eminetto/clean-architecture-go-v2/usecase/book/mock" + umock "github.com/eminetto/clean-architecture-go-v2/usecase/user/mock" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) @@ -25,9 +24,9 @@ func Test_Borrow(t *testing.T) { b := &entity.Book{ ID: entity.NewID(), } - uMock.EXPECT().GetUser(u.ID).Return(nil, domain.ErrNotFound) + uMock.EXPECT().GetUser(u.ID).Return(nil, entity.ErrNotFound) err := uc.Borrow(u, b) - assert.Equal(t, domain.ErrNotFound, err) + assert.Equal(t, entity.ErrNotFound, err) }) t.Run("book not found", func(t *testing.T) { u := &entity.User{ @@ -37,9 +36,9 @@ func Test_Borrow(t *testing.T) { ID: entity.NewID(), } uMock.EXPECT().GetUser(u.ID).Return(u, nil) - bMock.EXPECT().GetBook(b.ID).Return(nil, domain.ErrNotFound) + bMock.EXPECT().GetBook(b.ID).Return(nil, entity.ErrNotFound) err := uc.Borrow(u, b) - assert.Equal(t, domain.ErrNotFound, err) + assert.Equal(t, entity.ErrNotFound, err) }) t.Run("not enough books to borrow", func(t *testing.T) { u := &entity.User{ @@ -52,7 +51,7 @@ func Test_Borrow(t *testing.T) { uMock.EXPECT().GetUser(u.ID).Return(u, nil) bMock.EXPECT().GetBook(b.ID).Return(b, nil) err := uc.Borrow(u, b) - assert.Equal(t, domain.ErrNotEnoughBooks, err) + assert.Equal(t, entity.ErrNotEnoughBooks, err) }) t.Run("book already borrowed", func(t *testing.T) { u := &entity.User{ @@ -66,7 +65,7 @@ func Test_Borrow(t *testing.T) { uMock.EXPECT().GetUser(u.ID).Return(u, nil) bMock.EXPECT().GetBook(b.ID).Return(b, nil) err := uc.Borrow(u, b) - assert.Equal(t, domain.ErrBookAlreadyBorrowed, err) + assert.Equal(t, entity.ErrBookAlreadyBorrowed, err) }) t.Run("sucess", func(t *testing.T) { u := &entity.User{ @@ -95,9 +94,9 @@ func Test_Return(t *testing.T) { b := &entity.Book{ ID: entity.NewID(), } - bMock.EXPECT().GetBook(b.ID).Return(nil, domain.ErrNotFound) + bMock.EXPECT().GetBook(b.ID).Return(nil, entity.ErrNotFound) err := uc.Return(b) - assert.Equal(t, domain.ErrNotFound, err) + assert.Equal(t, entity.ErrNotFound, err) }) t.Run("book not borrowed", func(t *testing.T) { u := &entity.User{ @@ -109,7 +108,7 @@ func Test_Return(t *testing.T) { bMock.EXPECT().GetBook(b.ID).Return(b, nil) uMock.EXPECT().ListUsers().Return([]*entity.User{u}, nil) err := uc.Return(b) - assert.Equal(t, domain.ErrBookNotBorrowed, err) + assert.Equal(t, entity.ErrBookNotBorrowed, err) }) t.Run("success", func(t *testing.T) { u := &entity.User{ diff --git a/domain/usecase/user/interface.go b/usecase/user/interface.go similarity index 92% rename from domain/usecase/user/interface.go rename to usecase/user/interface.go index f673510..340205a 100644 --- a/domain/usecase/user/interface.go +++ b/usecase/user/interface.go @@ -1,7 +1,7 @@ package user import ( - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" ) //Reader interface diff --git a/domain/usecase/user/mock/user.go b/usecase/user/mock/user.go similarity index 99% rename from domain/usecase/user/mock/user.go rename to usecase/user/mock/user.go index 94d58dd..3a8e2df 100644 --- a/domain/usecase/user/mock/user.go +++ b/usecase/user/mock/user.go @@ -1,11 +1,11 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: domain/usecase/user/interface.go +// Source: usecase/user/interface.go // Package mock is a generated GoMock package. package mock import ( - entity "github.com/eminetto/clean-architecture-go-v2/domain/entity" + entity "github.com/eminetto/clean-architecture-go-v2/entity" gomock "github.com/golang/mock/gomock" reflect "reflect" ) diff --git a/domain/usecase/user/service.go b/usecase/user/service.go similarity index 85% rename from domain/usecase/user/service.go rename to usecase/user/service.go index e43861b..2f257b9 100644 --- a/domain/usecase/user/service.go +++ b/usecase/user/service.go @@ -4,9 +4,7 @@ import ( "strings" "time" - "github.com/eminetto/clean-architecture-go-v2/domain" - - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" ) //Service interface @@ -49,13 +47,13 @@ func (s *Service) ListUsers() ([]*entity.User, error) { func (s *Service) DeleteUser(id entity.ID) error { u, err := s.GetUser(id) if u == nil { - return domain.ErrNotFound + return entity.ErrNotFound } if err != nil { return err } if len(u.Books) > 0 { - return domain.ErrCannotBeDeleted + return entity.ErrCannotBeDeleted } return s.repo.Delete(id) } @@ -64,7 +62,7 @@ func (s *Service) DeleteUser(id entity.ID) error { func (s *Service) UpdateUser(e *entity.User) error { err := e.Validate() if err != nil { - return domain.ErrInvalidEntity + return entity.ErrInvalidEntity } e.UpdatedAt = time.Now() return s.repo.Update(e) diff --git a/domain/usecase/user/service_test.go b/usecase/user/service_test.go similarity index 89% rename from domain/usecase/user/service_test.go rename to usecase/user/service_test.go index a46dfda..086d8ac 100644 --- a/domain/usecase/user/service_test.go +++ b/usecase/user/service_test.go @@ -6,8 +6,7 @@ import ( "github.com/eminetto/clean-architecture-go-v2/infra/repository" - "github.com/eminetto/clean-architecture-go-v2/domain" - "github.com/eminetto/clean-architecture-go-v2/domain/entity" + "github.com/eminetto/clean-architecture-go-v2/entity" "github.com/stretchr/testify/assert" ) @@ -50,7 +49,7 @@ func Test_SearchAndFind(t *testing.T) { assert.Equal(t, "Osbourne", c[0].LastName) c, err = m.SearchUsers("dio") - assert.Equal(t, domain.ErrNotFound, err) + assert.Equal(t, entity.ErrNotFound, err) assert.Nil(t, c) }) t.Run("list all", func(t *testing.T) { @@ -91,12 +90,12 @@ func TestDelete(t *testing.T) { u2ID, _ := m.CreateUser(u2.Email, u2.Password, u2.FirstName, u2.LastName) err := m.DeleteUser(u1.ID) - assert.Equal(t, domain.ErrNotFound, err) + assert.Equal(t, entity.ErrNotFound, err) err = m.DeleteUser(u2ID) assert.Nil(t, err) _, err = m.GetUser(u2ID) - assert.Equal(t, domain.ErrNotFound, err) + assert.Equal(t, entity.ErrNotFound, err) u3 := newFixtureUser() id, _ := m.CreateUser(u3.Email, u3.Password, u3.FirstName, u3.LastName) @@ -104,5 +103,5 @@ func TestDelete(t *testing.T) { saved.Books = []entity.ID{entity.NewID()} _ = m.UpdateUser(saved) err = m.DeleteUser(id) - assert.Equal(t, domain.ErrCannotBeDeleted, err) + assert.Equal(t, entity.ErrCannotBeDeleted, err) } From 34b252f708109c87a5ad95becf770687193d0fa9 Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Mon, 12 Oct 2020 08:02:35 -0300 Subject: [PATCH 11/12] feat: move infra to driver --- api/main.go | 2 +- cmd/main.go | 2 +- docker-compose.yml | 4 ++-- {infra => driver}/repository/book_mysql.go | 0 {infra => driver}/repository/user_mysql.go | 0 {infra => ops}/db/init.sql | 0 {infra => ops}/prometheus/prometheus.yml | 0 .../book_inmem.go => usecase/book/inmem.go | 24 +++++++++---------- usecase/book/service_test.go | 9 ++++--- .../user_inmem.go => usecase/user/inmem.go | 24 +++++++++---------- usecase/user/service_test.go | 10 ++++---- 11 files changed, 36 insertions(+), 39 deletions(-) rename {infra => driver}/repository/book_mysql.go (100%) rename {infra => driver}/repository/user_mysql.go (100%) rename {infra => ops}/db/init.sql (100%) rename {infra => ops}/prometheus/prometheus.yml (100%) rename infra/repository/book_inmem.go => usecase/book/inmem.go (60%) rename infra/repository/user_inmem.go => usecase/user/inmem.go (62%) diff --git a/api/main.go b/api/main.go index 3c49d9c..20ab983 100644 --- a/api/main.go +++ b/api/main.go @@ -9,7 +9,7 @@ import ( "strconv" "time" - "github.com/eminetto/clean-architecture-go-v2/infra/repository" + "github.com/eminetto/clean-architecture-go-v2/driver/repository" "github.com/eminetto/clean-architecture-go-v2/usecase/book" "github.com/eminetto/clean-architecture-go-v2/usecase/user" diff --git a/cmd/main.go b/cmd/main.go index dfa9c23..f1041d7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,7 +7,7 @@ import ( "log" "os" - "github.com/eminetto/clean-architecture-go-v2/infra/repository" + "github.com/eminetto/clean-architecture-go-v2/driver/repository" "github.com/eminetto/clean-architecture-go-v2/usecase/book" "github.com/eminetto/clean-architecture-go-v2/config" diff --git a/docker-compose.yml b/docker-compose.yml index 6894b3b..82d0aab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: image: mysql:5.7 command: --default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql volumes: - - ./infra/init.sql:/data/application/init.sql + - ./ops/init.sql:/data/application/init.sql environment: MYSQL_ROOT_PASSWORD: clean_architecture_go_v2 MYSQL_DATABASE: clean_architecture_go_v2 @@ -30,7 +30,7 @@ services: command: - --config.file=/etc/prometheus/prometheus.yml volumes: - - ./infra/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ./ops/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro container_name: clean-architecture-go-v2-prometheus network_mode: "bridge" prometheus-pushgateway: diff --git a/infra/repository/book_mysql.go b/driver/repository/book_mysql.go similarity index 100% rename from infra/repository/book_mysql.go rename to driver/repository/book_mysql.go diff --git a/infra/repository/user_mysql.go b/driver/repository/user_mysql.go similarity index 100% rename from infra/repository/user_mysql.go rename to driver/repository/user_mysql.go diff --git a/infra/db/init.sql b/ops/db/init.sql similarity index 100% rename from infra/db/init.sql rename to ops/db/init.sql diff --git a/infra/prometheus/prometheus.yml b/ops/prometheus/prometheus.yml similarity index 100% rename from infra/prometheus/prometheus.yml rename to ops/prometheus/prometheus.yml diff --git a/infra/repository/book_inmem.go b/usecase/book/inmem.go similarity index 60% rename from infra/repository/book_inmem.go rename to usecase/book/inmem.go index 6dc393f..c9c9e5e 100644 --- a/infra/repository/book_inmem.go +++ b/usecase/book/inmem.go @@ -1,4 +1,4 @@ -package repository +package book import ( "strings" @@ -6,27 +6,27 @@ import ( "github.com/eminetto/clean-architecture-go-v2/entity" ) -//BookInmem in memory repo -type BookInmem struct { +//inmem in memory repo +type inmem struct { m map[entity.ID]*entity.Book } -//NewBookInmem create new repository -func NewBookInmem() *BookInmem { +//newInmem create new repository +func newInmem() *inmem { var m = map[entity.ID]*entity.Book{} - return &BookInmem{ + return &inmem{ m: m, } } //Create a book -func (r *BookInmem) Create(e *entity.Book) (entity.ID, error) { +func (r *inmem) Create(e *entity.Book) (entity.ID, error) { r.m[e.ID] = e return e.ID, nil } //Get a book -func (r *BookInmem) Get(id entity.ID) (*entity.Book, error) { +func (r *inmem) Get(id entity.ID) (*entity.Book, error) { if r.m[id] == nil { return nil, entity.ErrNotFound } @@ -34,7 +34,7 @@ func (r *BookInmem) Get(id entity.ID) (*entity.Book, error) { } //Update a book -func (r *BookInmem) Update(e *entity.Book) error { +func (r *inmem) Update(e *entity.Book) error { _, err := r.Get(e.ID) if err != nil { return err @@ -44,7 +44,7 @@ func (r *BookInmem) Update(e *entity.Book) error { } //Search books -func (r *BookInmem) Search(query string) ([]*entity.Book, error) { +func (r *inmem) Search(query string) ([]*entity.Book, error) { var d []*entity.Book for _, j := range r.m { if strings.Contains(strings.ToLower(j.Title), query) { @@ -55,7 +55,7 @@ func (r *BookInmem) Search(query string) ([]*entity.Book, error) { } //List books -func (r *BookInmem) List() ([]*entity.Book, error) { +func (r *inmem) List() ([]*entity.Book, error) { var d []*entity.Book for _, j := range r.m { d = append(d, j) @@ -64,7 +64,7 @@ func (r *BookInmem) List() ([]*entity.Book, error) { } //Delete a book -func (r *BookInmem) Delete(id entity.ID) error { +func (r *inmem) Delete(id entity.ID) error { if r.m[id] == nil { return entity.ErrNotFound } diff --git a/usecase/book/service_test.go b/usecase/book/service_test.go index 47ffbe8..999b5b6 100644 --- a/usecase/book/service_test.go +++ b/usecase/book/service_test.go @@ -5,7 +5,6 @@ import ( "time" "github.com/eminetto/clean-architecture-go-v2/entity" - "github.com/eminetto/clean-architecture-go-v2/infra/repository" "github.com/stretchr/testify/assert" ) @@ -21,7 +20,7 @@ func newFixtureBook() *entity.Book { } func Test_Create(t *testing.T) { - repo := repository.NewBookInmem() + repo := newInmem() m := NewService(repo) u := newFixtureBook() _, err := m.CreateBook(u.Title, u.Author, u.Pages, u.Quantity) @@ -30,7 +29,7 @@ func Test_Create(t *testing.T) { } func Test_SearchAndFind(t *testing.T) { - repo := repository.NewBookInmem() + repo := newInmem() m := NewService(repo) u1 := newFixtureBook() u2 := newFixtureBook() @@ -63,7 +62,7 @@ func Test_SearchAndFind(t *testing.T) { } func Test_Update(t *testing.T) { - repo := repository.NewBookInmem() + repo := newInmem() m := NewService(repo) u := newFixtureBook() id, err := m.CreateBook(u.Title, u.Author, u.Pages, u.Quantity) @@ -77,7 +76,7 @@ func Test_Update(t *testing.T) { } func TestDelete(t *testing.T) { - repo := repository.NewBookInmem() + repo := newInmem() m := NewService(repo) u1 := newFixtureBook() u2 := newFixtureBook() diff --git a/infra/repository/user_inmem.go b/usecase/user/inmem.go similarity index 62% rename from infra/repository/user_inmem.go rename to usecase/user/inmem.go index 0ffd08d..651c6d4 100644 --- a/infra/repository/user_inmem.go +++ b/usecase/user/inmem.go @@ -1,4 +1,4 @@ -package repository +package user import ( "fmt" @@ -7,27 +7,27 @@ import ( "github.com/eminetto/clean-architecture-go-v2/entity" ) -//UserInmem in memory repo -type UserInmem struct { +//inmem in memory repo +type inmem struct { m map[entity.ID]*entity.User } -//NewUserInmem create new repository -func NewUserInmem() *UserInmem { +//newInmem create new repository +func newInmem() *inmem { var m = map[entity.ID]*entity.User{} - return &UserInmem{ + return &inmem{ m: m, } } //Create an user -func (r *UserInmem) Create(e *entity.User) (entity.ID, error) { +func (r *inmem) Create(e *entity.User) (entity.ID, error) { r.m[e.ID] = e return e.ID, nil } //Get an user -func (r *UserInmem) Get(id entity.ID) (*entity.User, error) { +func (r *inmem) Get(id entity.ID) (*entity.User, error) { if r.m[id] == nil { return nil, entity.ErrNotFound } @@ -35,7 +35,7 @@ func (r *UserInmem) Get(id entity.ID) (*entity.User, error) { } //Update an user -func (r *UserInmem) Update(e *entity.User) error { +func (r *inmem) Update(e *entity.User) error { _, err := r.Get(e.ID) if err != nil { return err @@ -45,7 +45,7 @@ func (r *UserInmem) Update(e *entity.User) error { } //Search users -func (r *UserInmem) Search(query string) ([]*entity.User, error) { +func (r *inmem) Search(query string) ([]*entity.User, error) { var d []*entity.User for _, j := range r.m { if strings.Contains(strings.ToLower(j.FirstName), query) { @@ -60,7 +60,7 @@ func (r *UserInmem) Search(query string) ([]*entity.User, error) { } //List users -func (r *UserInmem) List() ([]*entity.User, error) { +func (r *inmem) List() ([]*entity.User, error) { var d []*entity.User for _, j := range r.m { d = append(d, j) @@ -69,7 +69,7 @@ func (r *UserInmem) List() ([]*entity.User, error) { } //Delete an user -func (r *UserInmem) Delete(id entity.ID) error { +func (r *inmem) Delete(id entity.ID) error { if r.m[id] == nil { return fmt.Errorf("not found") } diff --git a/usecase/user/service_test.go b/usecase/user/service_test.go index 086d8ac..99fafc4 100644 --- a/usecase/user/service_test.go +++ b/usecase/user/service_test.go @@ -4,8 +4,6 @@ import ( "testing" "time" - "github.com/eminetto/clean-architecture-go-v2/infra/repository" - "github.com/eminetto/clean-architecture-go-v2/entity" "github.com/stretchr/testify/assert" @@ -23,7 +21,7 @@ func newFixtureUser() *entity.User { } func Test_Create(t *testing.T) { - repo := repository.NewUserInmem() + repo := newInmem() m := NewService(repo) u := newFixtureUser() _, err := m.CreateUser(u.Email, u.Password, u.FirstName, u.LastName) @@ -33,7 +31,7 @@ func Test_Create(t *testing.T) { } func Test_SearchAndFind(t *testing.T) { - repo := repository.NewUserInmem() + repo := newInmem() m := NewService(repo) u1 := newFixtureUser() u2 := newFixtureUser() @@ -66,7 +64,7 @@ func Test_SearchAndFind(t *testing.T) { } func Test_Update(t *testing.T) { - repo := repository.NewUserInmem() + repo := newInmem() m := NewService(repo) u := newFixtureUser() id, err := m.CreateUser(u.Email, u.Password, u.FirstName, u.LastName) @@ -83,7 +81,7 @@ func Test_Update(t *testing.T) { } func TestDelete(t *testing.T) { - repo := repository.NewUserInmem() + repo := newInmem() m := NewService(repo) u1 := newFixtureUser() u2 := newFixtureUser() From b1020aef90782d003b4bcbed8bb856f0f6a37768 Mon Sep 17 00:00:00 2001 From: Elton Minetto Date: Mon, 12 Oct 2020 08:16:08 -0300 Subject: [PATCH 12/12] fix: add comments --- entity/entity.go | 3 +++ entity/user.go | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/entity/entity.go b/entity/entity.go index f1f0396..9c99501 100644 --- a/entity/entity.go +++ b/entity/entity.go @@ -2,12 +2,15 @@ package entity import "github.com/google/uuid" +//ID entity ID type ID = uuid.UUID +//NewID create a new entity ID func NewID() ID { return ID(uuid.New()) } +//StringToID convert a string to an entity ID func StringToID(s string) (ID, error) { id, err := uuid.Parse(s) return ID(id), err diff --git a/entity/user.go b/entity/user.go index 963da6a..fbb8a9e 100644 --- a/entity/user.go +++ b/entity/user.go @@ -79,7 +79,7 @@ func (u *User) Validate() error { return nil } -//ValidatePassword +//ValidatePassword validate user password func (u *User) ValidatePassword(p string) error { err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(p)) if err != nil {