diff --git a/api/handler/book.go b/api/handler/book.go index 2266243..a585c58 100644 --- a/api/handler/book.go +++ b/api/handler/book.go @@ -17,7 +17,7 @@ import ( "github.com/gorilla/mux" ) -func listBooks(service book.UseCase) http.Handler { +func listBooks(service book.Repository) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error reading books" var data []*book.Book @@ -58,7 +58,7 @@ func listBooks(service book.UseCase) http.Handler { }) } -func createBook(service book.UseCase) http.Handler { +func createBook(service book.Repository) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error adding book" var input struct { @@ -107,7 +107,7 @@ func createBook(service book.UseCase) http.Handler { }) } -func getBook(service book.UseCase) http.Handler { +func getBook(service book.Repository) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error reading book" vars := mux.Vars(r) @@ -143,7 +143,7 @@ func getBook(service book.UseCase) http.Handler { }) } -func deleteBook(service book.UseCase) http.Handler { +func deleteBook(service book.Repository) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error removing bookmark" vars := mux.Vars(r) @@ -163,7 +163,7 @@ func deleteBook(service book.UseCase) http.Handler { } //MakeBookHandlers make url handlers -func MakeBookHandlers(r *mux.Router, n negroni.Negroni, service book.UseCase) { +func MakeBookHandlers(r *mux.Router, n negroni.Negroni, service book.Repository) { r.Handle("/v1/book", n.With( negroni.Wrap(listBooks(service)), )).Methods("GET", "OPTIONS").Name("listBooks") diff --git a/api/handler/loan.go b/api/handler/loan.go index d1f1b82..ae3c3ba 100644 --- a/api/handler/loan.go +++ b/api/handler/loan.go @@ -6,7 +6,7 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" - "github.com/eminetto/clean-architecture-go-v2/domain/loan" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/loan" "github.com/eminetto/clean-architecture-go-v2/domain" @@ -17,7 +17,7 @@ import ( "github.com/gorilla/mux" ) -func borrowBook(bService book.UseCase, uService user.UseCase, loanService loan.UseCase) http.Handler { +func borrowBook(bService book.Repository, uService user.Repository, loanService loan.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error borrowing book" vars := mux.Vars(r) @@ -66,7 +66,7 @@ func borrowBook(bService book.UseCase, uService user.UseCase, loanService loan.U }) } -func returnBook(bService book.UseCase, loanService loan.UseCase) http.Handler { +func returnBook(bService book.Repository, loanService loan.UseCase) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error returning book" vars := mux.Vars(r) @@ -98,7 +98,7 @@ func returnBook(bService book.UseCase, loanService loan.UseCase) http.Handler { } //MakeLoanHandlers make url handlers -func MakeLoanHandlers(r *mux.Router, n negroni.Negroni, bService book.UseCase, uService user.UseCase, loanService loan.UseCase) { +func MakeLoanHandlers(r *mux.Router, n negroni.Negroni, bService book.Repository, uService user.Repository, loanService loan.UseCase) { r.Handle("/v1/loan/borrow/{book_id}/{user_id}", n.With( negroni.Wrap(borrowBook(bService, uService, loanService)), )).Methods("GET", "OPTIONS").Name("borrowBook") diff --git a/api/handler/loan_test.go b/api/handler/loan_test.go index 6c0ef80..7075dce 100644 --- a/api/handler/loan_test.go +++ b/api/handler/loan_test.go @@ -16,7 +16,7 @@ import ( "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" - lmock "github.com/eminetto/clean-architecture-go-v2/domain/loan/mock" + lmock "github.com/eminetto/clean-architecture-go-v2/domain/usecase/loan/mock" "github.com/golang/mock/gomock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" diff --git a/api/handler/user.go b/api/handler/user.go index 6043c36..784ed97 100644 --- a/api/handler/user.go +++ b/api/handler/user.go @@ -18,7 +18,7 @@ import ( "github.com/gorilla/mux" ) -func listUsers(service user.UseCase) http.Handler { +func listUsers(service user.Repository) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error reading users" var data []*user.User @@ -58,7 +58,7 @@ func listUsers(service user.UseCase) http.Handler { }) } -func createUser(service user.UseCase) http.Handler { +func createUser(service user.Repository) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error adding user" var input struct { @@ -107,7 +107,7 @@ func createUser(service user.UseCase) http.Handler { }) } -func getUser(service user.UseCase) http.Handler { +func getUser(service user.Repository) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error reading user" vars := mux.Vars(r) @@ -143,7 +143,7 @@ func getUser(service user.UseCase) http.Handler { }) } -func deleteUser(service user.UseCase) http.Handler { +func deleteUser(service user.Repository) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { errorMessage := "Error removing user" vars := mux.Vars(r) @@ -163,7 +163,7 @@ func deleteUser(service user.UseCase) http.Handler { } //MakeUserHandlers make url handlers -func MakeUserHandlers(r *mux.Router, n negroni.Negroni, service user.UseCase) { +func MakeUserHandlers(r *mux.Router, n negroni.Negroni, service user.Repository) { r.Handle("/v1/user", n.With( negroni.Wrap(listUsers(service)), )).Methods("GET", "OPTIONS").Name("listUsers") diff --git a/api/main.go b/api/main.go index 6aa4be4..acc03c0 100644 --- a/api/main.go +++ b/api/main.go @@ -11,7 +11,7 @@ import ( "github.com/eminetto/clean-architecture-go-v2/pkg/password" - "github.com/eminetto/clean-architecture-go-v2/domain/loan" + "github.com/eminetto/clean-architecture-go-v2/domain/usecase/loan" "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" @@ -38,12 +38,12 @@ func main() { defer db.Close() bookRepo := book.NewMySQLRepoRepository(db) - bookService := book.NewService(bookRepo) + bookService := book.NewRepository(bookRepo) userRepo := user.NewMySQLRepoRepository(db) - userService := user.NewService(userRepo, password.NewService()) + userService := user.NewRepository(userRepo, password.NewService()) - loanService := loan.NewService(userService, bookService) + loanService := loan.NewUseCase(userService, bookService) metricService, err := metric.NewPrometheusService() if err != nil { diff --git a/cmd/main.go b/cmd/main.go index d4ab153..00ddbea 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -41,7 +41,7 @@ func main() { } defer db.Close() repo := book.NewMySQLRepoRepository(db) - service := book.NewService(repo) + service := book.NewRepository(repo) all, err := service.Search(query) if err != nil { log.Fatal(err) diff --git a/domain/entity/book/book.go b/domain/entity/book/entity.go similarity index 100% rename from domain/entity/book/book.go rename to domain/entity/book/entity.go diff --git a/domain/entity/book/interface.go b/domain/entity/book/interface.go index 2496a28..cfbe549 100644 --- a/domain/entity/book/interface.go +++ b/domain/entity/book/interface.go @@ -21,9 +21,3 @@ type Repository interface { Reader Writer } - -//UseCase use case interface -type UseCase interface { - Reader - Writer -} \ No newline at end of file diff --git a/domain/entity/book/service.go b/domain/entity/book/repository.go similarity index 53% rename from domain/entity/book/service.go rename to domain/entity/book/repository.go index 682c105..6eecc32 100644 --- a/domain/entity/book/service.go +++ b/domain/entity/book/repository.go @@ -7,42 +7,41 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) -//Service service interface -type Service struct { +type repository struct { repo Repository } -//NewService create new use case -func NewService(r Repository) *Service { - return &Service{ +//NewRepository create new repository +func NewRepository(r Repository) *repository { + return &repository{ repo: r, } } //Create a book -func (s *Service) Create(e *Book) (entity.ID, error) { +func (s *repository) Create(e *Book) (entity.ID, error) { e.ID = entity.NewID() e.CreatedAt = time.Now() return s.repo.Create(e) } //Get a book -func (s *Service) Get(id entity.ID) (*Book, error) { +func (s *repository) Get(id entity.ID) (*Book, error) { return s.repo.Get(id) } //Search books -func (s *Service) Search(query string) ([]*Book, error) { +func (s *repository) Search(query string) ([]*Book, error) { return s.repo.Search(strings.ToLower(query)) } //List books -func (s *Service) List() ([]*Book, error) { +func (s *repository) List() ([]*Book, error) { return s.repo.List() } //Delete a book -func (s *Service) Delete(id entity.ID) error { +func (s *repository) Delete(id entity.ID) error { _, err := s.Get(id) if err != nil { return err @@ -51,6 +50,6 @@ func (s *Service) Delete(id entity.ID) error { } //Update a book -func (s *Service) Update(e *Book) error { +func (s *repository) Update(e *Book) error { return s.repo.Update(e) } diff --git a/domain/entity/book/repository_inmem.go b/domain/entity/book/repository_inmem.go index 8e5a788..1410bea 100644 --- a/domain/entity/book/repository_inmem.go +++ b/domain/entity/book/repository_inmem.go @@ -7,27 +7,27 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) -//IRepo in memory repo -type IRepo struct { +//iRepo in memory repo +type iRepo struct { m map[entity.ID]*Book } //NewInmemRepository create new repository -func NewInmemRepository() *IRepo { +func NewInmemRepository() *iRepo { var m = map[entity.ID]*Book{} - return &IRepo{ + return &iRepo{ m: m, } } //Create a book -func (r *IRepo) Create(e *Book) (entity.ID, error) { +func (r *iRepo) Create(e *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 *iRepo) Get(id entity.ID) (*Book, error) { if r.m[id] == nil { return nil, domain.ErrNotFound } @@ -35,7 +35,7 @@ func (r *IRepo) Get(id entity.ID) (*Book, error) { } //Update a book -func (r *IRepo) Update(e *Book) error { +func (r *iRepo) Update(e *Book) error { _, err := r.Get(e.ID) if err != nil { return err @@ -45,7 +45,7 @@ func (r *IRepo) Update(e *Book) error { } //Search books -func (r *IRepo) Search(query string) ([]*Book, error) { +func (r *iRepo) Search(query string) ([]*Book, error) { var d []*Book for _, j := range r.m { if strings.Contains(strings.ToLower(j.Title), query) { @@ -60,7 +60,7 @@ func (r *IRepo) Search(query string) ([]*Book, error) { } //List books -func (r *IRepo) List() ([]*Book, error) { +func (r *iRepo) List() ([]*Book, error) { var d []*Book for _, j := range r.m { d = append(d, j) @@ -69,7 +69,7 @@ func (r *IRepo) List() ([]*Book, error) { } //Delete a book -func (r *IRepo) Delete(id entity.ID) error { +func (r *iRepo) Delete(id entity.ID) error { if r.m[id] == nil { return domain.ErrNotFound } diff --git a/domain/entity/book/repository_mysql.go b/domain/entity/book/repository_mysql.go index ff345f6..4ab968d 100644 --- a/domain/entity/book/repository_mysql.go +++ b/domain/entity/book/repository_mysql.go @@ -9,20 +9,20 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) -//MySQLRepo mysql repo -type MySQLRepo struct { +//mySQLRepo mysql repo +type mySQLRepo struct { db *sql.DB } //NewMySQLRepoRepository create new repository -func NewMySQLRepoRepository(db *sql.DB) *MySQLRepo { - return &MySQLRepo{ +func NewMySQLRepoRepository(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 *Book) (entity.ID, error) { stmt, err := r.db.Prepare(` insert into book (id, title, author, pages, quantity, created_at) values(?,?,?,?,?,?)`) @@ -48,7 +48,7 @@ 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) (*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 @@ -65,7 +65,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 *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,7 +75,7 @@ func (r *MySQLRepo) Update(e *Book) error { } //Search books -func (r *MySQLRepo) Search(query string) ([]*Book, error) { +func (r *mySQLRepo) Search(query string) ([]*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 @@ -100,7 +100,7 @@ func (r *MySQLRepo) Search(query string) ([]*Book, error) { } //List books -func (r *MySQLRepo) List() ([]*Book, error) { +func (r *mySQLRepo) List() ([]*Book, error) { stmt, err := r.db.Prepare(`select id, title, author, pages, quantity, created_at from book`) if err != nil { return nil, err @@ -125,7 +125,7 @@ func (r *MySQLRepo) List() ([]*Book, error) { } //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/entity/book/service_test.go b/domain/entity/book/repository_test.go similarity index 93% rename from domain/entity/book/service_test.go rename to domain/entity/book/repository_test.go index 10afb9e..85e91f3 100644 --- a/domain/entity/book/service_test.go +++ b/domain/entity/book/repository_test.go @@ -10,7 +10,7 @@ import ( func Test_Create(t *testing.T) { repo := NewInmemRepository() - service := NewService(repo) + service := NewRepository(repo) u := NewFixtureBook() id, err := service.Create(u) assert.Nil(t, err) @@ -20,7 +20,7 @@ func Test_Create(t *testing.T) { func Test_SearchAndFind(t *testing.T) { repo := NewInmemRepository() - service := NewService(repo) + service := NewRepository(repo) u1 := NewFixtureBook() u2 := NewFixtureBook() u2.Title = "Lemmy: Biography" @@ -53,7 +53,7 @@ func Test_SearchAndFind(t *testing.T) { func Test_Update(t *testing.T) { repo := NewInmemRepository() - service := NewService(repo) + service := NewRepository(repo) u := NewFixtureBook() id, err := service.Create(u) assert.Nil(t, err) @@ -67,7 +67,7 @@ func Test_Update(t *testing.T) { func TestDelete(t *testing.T) { repo := NewInmemRepository() - service := NewService(repo) + service := NewRepository(repo) u1 := NewFixtureBook() u2 := NewFixtureBook() u2ID, _ := service.Create(u2) diff --git a/domain/entity/user/user.go b/domain/entity/user/entity.go similarity index 100% rename from domain/entity/user/user.go rename to domain/entity/user/entity.go diff --git a/domain/entity/user/interface.go b/domain/entity/user/interface.go index eb5f309..b76c6ea 100644 --- a/domain/entity/user/interface.go +++ b/domain/entity/user/interface.go @@ -21,9 +21,3 @@ type Repository interface { Reader Writer } - -//UseCase use case interface -type UseCase interface { - Reader - Writer -} \ No newline at end of file diff --git a/domain/entity/user/service.go b/domain/entity/user/repository.go similarity index 53% rename from domain/entity/user/service.go rename to domain/entity/user/repository.go index a476849..47d5196 100644 --- a/domain/entity/user/service.go +++ b/domain/entity/user/repository.go @@ -4,27 +4,29 @@ import ( "strings" "time" + "github.com/eminetto/clean-architecture-go-v2/domain" + "github.com/eminetto/clean-architecture-go-v2/pkg/password" "github.com/eminetto/clean-architecture-go-v2/domain/entity" ) -//Service service interface -type Service struct { +//repository service interface +type repository struct { repo Repository pwd password.UseCase } -//NewService create new use case -func NewService(r Repository, pwd password.UseCase) *Service { - return &Service{ +//NewRepository create new repository +func NewRepository(r Repository, pwd password.UseCase) *repository { + return &repository{ repo: r, pwd: pwd, } } //Create an user -func (s *Service) Create(e *User) (entity.ID, error) { +func (s *repository) Create(e *User) (entity.ID, error) { e.ID = entity.NewID() e.CreatedAt = time.Now() pwd, err := s.pwd.Generate(e.Password) @@ -36,31 +38,34 @@ func (s *Service) Create(e *User) (entity.ID, error) { } //Get an user -func (s *Service) Get(id entity.ID) (*User, error) { +func (s *repository) Get(id entity.ID) (*User, error) { return s.repo.Get(id) } //Search users -func (s *Service) Search(query string) ([]*User, error) { +func (s *repository) Search(query string) ([]*User, error) { return s.repo.Search(strings.ToLower(query)) } //List users -func (s *Service) List() ([]*User, error) { +func (s *repository) List() ([]*User, error) { return s.repo.List() } //Delete an user -func (s *Service) Delete(id entity.ID) error { - _, err := s.Get(id) +func (s *repository) Delete(id entity.ID) error { + u, err := s.Get(id) if err != nil { return err } + if len(u.Books) > 0 { + return domain.ErrCannotBeDeleted + } return s.repo.Delete(id) } //Update an user -func (s *Service) Update(e *User) error { +func (s *repository) Update(e *User) error { e.UpdatedAt = time.Now() return s.repo.Update(e) } diff --git a/domain/entity/user/service_test.go b/domain/entity/user/repository_test.go similarity index 89% rename from domain/entity/user/service_test.go rename to domain/entity/user/repository_test.go index 4c6f004..8f7a1c4 100644 --- a/domain/entity/user/service_test.go +++ b/domain/entity/user/repository_test.go @@ -13,7 +13,7 @@ import ( func Test_Create(t *testing.T) { repo := NewInmemRepository() - service := NewService(repo, password.NewFakeService()) + service := NewRepository(repo, password.NewFakeService()) u := NewFixtureUser() id, err := service.Create(u) assert.Nil(t, err) @@ -24,7 +24,7 @@ func Test_Create(t *testing.T) { func Test_SearchAndFind(t *testing.T) { repo := NewInmemRepository() - service := NewService(repo, password.NewFakeService()) + service := NewRepository(repo, password.NewFakeService()) u1 := NewFixtureUser() u2 := NewFixtureUser() u2.FirstName = "Lemmy" @@ -57,7 +57,7 @@ func Test_SearchAndFind(t *testing.T) { func Test_Update(t *testing.T) { repo := NewInmemRepository() - service := NewService(repo, password.NewFakeService()) + service := NewRepository(repo, password.NewFakeService()) u := NewFixtureUser() id, err := service.Create(u) assert.Nil(t, err) @@ -74,7 +74,7 @@ func Test_Update(t *testing.T) { func TestDelete(t *testing.T) { repo := NewInmemRepository() - service := NewService(repo, password.NewFakeService()) + service := NewRepository(repo, password.NewFakeService()) u1 := NewFixtureUser() u2 := NewFixtureUser() u2ID, _ := service.Create(u2) diff --git a/domain/loan/interface.go b/domain/usecase/loan/interface.go similarity index 100% rename from domain/loan/interface.go rename to domain/usecase/loan/interface.go diff --git a/domain/loan/mock/loan.go b/domain/usecase/loan/mock/loan.go similarity index 100% rename from domain/loan/mock/loan.go rename to domain/usecase/loan/mock/loan.go diff --git a/domain/loan/service.go b/domain/usecase/loan/usecase.go similarity index 83% rename from domain/loan/service.go rename to domain/usecase/loan/usecase.go index 6c15d26..3c2acdb 100644 --- a/domain/loan/service.go +++ b/domain/usecase/loan/usecase.go @@ -7,22 +7,21 @@ import ( "github.com/eminetto/clean-architecture-go-v2/domain/entity/user" ) -//Service service interface -type Service struct { - uService user.UseCase - bService book.UseCase +type usecase struct { + uService user.Repository + bService book.Repository } -//NewService create new use case -func NewService(u user.UseCase, b book.UseCase) *Service { - return &Service{ +//NewUseCase create new use case +func NewUseCase(u user.Repository, b book.Repository) *usecase { + return &usecase{ uService: u, bService: b, } } //Borrow borrow a book to an user -func (s *Service) Borrow(u *user.User, b *book.Book) error { +func (s *usecase) Borrow(u *user.User, b *book.Book) error { u, err := s.uService.Get(u.ID) if err != nil { return err @@ -53,7 +52,7 @@ func (s *Service) Borrow(u *user.User, b *book.Book) error { } //Return return a book -func (s *Service) Return(b *book.Book) error { +func (s *usecase) Return(b *book.Book) error { b, err := s.bService.Get(b.ID) if err != nil { return err diff --git a/domain/loan/service_test.go b/domain/usecase/loan/usecase_test.go similarity index 97% rename from domain/loan/service_test.go rename to domain/usecase/loan/usecase_test.go index 020c545..1a33620 100644 --- a/domain/loan/service_test.go +++ b/domain/usecase/loan/usecase_test.go @@ -19,7 +19,7 @@ func Test_Borrow(t *testing.T) { defer controller.Finish() uMock := umock.NewMockUseCase(controller) bMock := bmock.NewMockUseCase(controller) - service := NewService(uMock, bMock) + service := NewUseCase(uMock, bMock) t.Run("user not found", func(t *testing.T) { u := user.NewFixtureUser() b := book.NewFixtureBook() @@ -71,7 +71,7 @@ func Test_Return(t *testing.T) { defer controller.Finish() uMock := umock.NewMockUseCase(controller) bMock := bmock.NewMockUseCase(controller) - service := NewService(uMock, bMock) + service := NewUseCase(uMock, bMock) t.Run("book not found", func(t *testing.T) { b := book.NewFixtureBook() bMock.EXPECT().Get(b.ID).Return(nil, domain.ErrNotFound)