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=