mirror of
https://github.com/rjNemo/go-microservices-tuto
synced 2026-06-06 02:16:46 +00:00
🧹 clean code structure
This commit is contained in:
parent
51520e2238
commit
7b50b02a04
5 changed files with 82 additions and 57 deletions
43
main.go
43
main.go
|
|
@ -3,54 +3,49 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/rjNemo/go-micro/handlers"
|
||||
|
||||
"github.com/rjNemo/go-micro/products/handlers"
|
||||
"github.com/rjNemo/go-micro/server"
|
||||
)
|
||||
|
||||
const port = ":5000"
|
||||
|
||||
func main() {
|
||||
// create a logger to control application wide logging
|
||||
logger := log.New(os.Stdout, "Product API: ", log.LstdFlags|log.Lshortfile)
|
||||
|
||||
// create the handlers
|
||||
productsHandler := handlers.NewProducts(logger)
|
||||
// create a server mux and register the handlers
|
||||
// create a router
|
||||
router := mux.NewRouter()
|
||||
// GET
|
||||
getRouter := router.Methods(http.MethodGet).Subrouter()
|
||||
getRouter.HandleFunc("/", productsHandler.GetProducts)
|
||||
// POST
|
||||
postRouter := router.Methods(http.MethodPost).Subrouter()
|
||||
postRouter.HandleFunc("/", productsHandler.AddProduct)
|
||||
postRouter.Use(productsHandler.ProductValidationMiddleware)
|
||||
// PUT
|
||||
putRouter := router.Methods(http.MethodPut).Subrouter()
|
||||
putRouter.HandleFunc("/{id:[0-9]+}", productsHandler.UpdateProduct)
|
||||
putRouter.Use(productsHandler.ProductValidationMiddleware)
|
||||
|
||||
// creates a new server
|
||||
// create the handler
|
||||
productsHandler := handlers.New(logger)
|
||||
|
||||
// register the handler method to the router
|
||||
productsHandler.RegisterRoutes(router)
|
||||
|
||||
// creates a production-ready server using the handler
|
||||
srv := server.New(router, port)
|
||||
|
||||
// non blocking application server
|
||||
// start a non blocking application server
|
||||
go func() {
|
||||
logger.Printf("Server started at address http://localhost%s...", port)
|
||||
logger.Fatalf("Server failed: %v", srv.ListenAndServe())
|
||||
logger.Printf("Server started at address http://localhost%s ...", port)
|
||||
logger.Fatalf("Server failed: %v", srv.ListenAndServe()) // TODO: use ListenAndServeTLS in production
|
||||
}()
|
||||
|
||||
// catch sigterm or interrupt and gracefully terminates the server
|
||||
sigChan := make(chan os.Signal)
|
||||
signal.Notify(sigChan, os.Interrupt)
|
||||
signal.Notify(sigChan, os.Kill)
|
||||
|
||||
signal.Notify(sigChan, os.Interrupt) // interrupt
|
||||
signal.Notify(sigChan, os.Kill) // sigterm
|
||||
// log received signal
|
||||
sig := <-sigChan
|
||||
logger.Printf("Received %v signal... graceful shutdown", sig)
|
||||
|
||||
toCtx, _ := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
toCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
cancel()
|
||||
srv.Shutdown(toCtx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
package data
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/rjNemo/go-micro/products/models"
|
||||
)
|
||||
|
||||
// dummy persistence layer
|
||||
var productList = []*Product{
|
||||
var productList = []*models.Product{
|
||||
{
|
||||
ID: 1,
|
||||
Name: "Latte",
|
||||
|
|
|
|||
|
|
@ -4,28 +4,18 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/rjNemo/go-micro/products/models"
|
||||
)
|
||||
|
||||
// Product defines the structure of a product
|
||||
type Product struct {
|
||||
ID int `json:"id"` //TODO: use uuid
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Price float32 `json:"price"` // TODO: use int
|
||||
SKU string `json:"sku"`
|
||||
CreatedOn string `json:"-"`
|
||||
UpdatedOn string `json:"-"`
|
||||
DeletedOn string `json:"-"`
|
||||
}
|
||||
|
||||
// FromJSON read JSON data to create a new product
|
||||
func (p *Product) FromJSON(r io.Reader) error {
|
||||
return json.NewDecoder(r).Decode(p)
|
||||
}
|
||||
|
||||
// Products is the collection of products.
|
||||
// It encapsulates data access logic
|
||||
type Products []*Product
|
||||
type Products []*models.Product
|
||||
|
||||
// ToJSON returns all existing product in JSON format
|
||||
func (p *Products) ToJSON(w io.Writer) error {
|
||||
return json.NewEncoder(w).Encode(p) // more efficient in memory and time than Marshal
|
||||
}
|
||||
|
||||
// AllProducts returns all existing products
|
||||
func AllProducts() Products {
|
||||
|
|
@ -33,13 +23,13 @@ func AllProducts() Products {
|
|||
}
|
||||
|
||||
// AddProduct add a Product to the dataStore
|
||||
func AddProduct(p *Product) {
|
||||
func AddProduct(p *models.Product) {
|
||||
p.ID = getNextID()
|
||||
productList = append(productList, p)
|
||||
}
|
||||
|
||||
// UpdateProduct edits a Product identified by its id
|
||||
func UpdateProduct(id int, p *Product) error {
|
||||
func UpdateProduct(id int, p *models.Product) error {
|
||||
idx, _, err := findProduct(id)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -53,7 +43,7 @@ func UpdateProduct(id int, p *Product) error {
|
|||
var ErrorProductNotFound = fmt.Errorf("Product not found")
|
||||
|
||||
// findProduct retrieves a product via its unique identifier
|
||||
func findProduct(id int) (int, *Product, error) {
|
||||
func findProduct(id int) (int, *models.Product, error) {
|
||||
for i, p := range productList {
|
||||
if p.ID == id {
|
||||
return i, p, nil
|
||||
|
|
@ -66,8 +56,3 @@ func findProduct(id int) (int, *Product, error) {
|
|||
func getNextID() int {
|
||||
return productList[len(productList)-1].ID + 1
|
||||
}
|
||||
|
||||
// ToJSON returns all existing product in JSON format
|
||||
func (p *Products) ToJSON(w io.Writer) error {
|
||||
return json.NewEncoder(w).Encode(p) // more efficient in memory and time than Marshal
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/rjNemo/go-micro/products/data"
|
||||
"github.com/rjNemo/go-micro/products/models"
|
||||
)
|
||||
|
||||
// Products is a handler for Products API service
|
||||
|
|
@ -16,8 +17,10 @@ type Products struct {
|
|||
logger *log.Logger
|
||||
}
|
||||
|
||||
// NewProducts creates a Products handler
|
||||
func NewProducts(logger *log.Logger) *Products { return &Products{logger: logger} }
|
||||
// New creates a Products handler
|
||||
func New(logger *log.Logger) *Products {
|
||||
return &Products{logger: logger}
|
||||
}
|
||||
|
||||
// GetProducts writes all products to response in JSON format
|
||||
func (p *Products) GetProducts(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
@ -37,7 +40,7 @@ func (p *Products) GetProducts(w http.ResponseWriter, r *http.Request) {
|
|||
func (p *Products) AddProduct(w http.ResponseWriter, r *http.Request) {
|
||||
p.logger.Println("Handle 'POST' request")
|
||||
// get product from the request
|
||||
newProd := r.Context().Value(KeyProduct{}).(*data.Product) // cast into a Product
|
||||
newProd := r.Context().Value(KeyProduct{}).(*models.Product) // cast into a Product
|
||||
|
||||
p.logger.Printf("product: %#v", newProd)
|
||||
data.AddProduct(newProd)
|
||||
|
|
@ -50,7 +53,7 @@ func (p *Products) UpdateProduct(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
p.logger.Println("Handle 'PUT' request", id)
|
||||
// get product from the request
|
||||
newProd := r.Context().Value(KeyProduct{}).(*data.Product) // cast into a Product
|
||||
newProd := r.Context().Value(KeyProduct{}).(*models.Product) // cast into a Product
|
||||
|
||||
p.logger.Printf("product: %#v", newProd)
|
||||
err := data.UpdateProduct(id, newProd)
|
||||
|
|
@ -72,7 +75,7 @@ type KeyProduct struct{}
|
|||
func (p *Products) ProductValidationMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// create a new product
|
||||
newProd := &data.Product{}
|
||||
newProd := &models.Product{}
|
||||
// deserialize JSON to product
|
||||
err := newProd.FromJSON(r.Body)
|
||||
if err != nil {
|
||||
|
|
@ -88,3 +91,18 @@ func (p *Products) ProductValidationMiddleware(next http.Handler) http.Handler {
|
|||
next.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterRoutes associates path to controller
|
||||
func (p *Products) RegisterRoutes(r *mux.Router) {
|
||||
// GET
|
||||
getRouter := r.Methods(http.MethodGet).Subrouter()
|
||||
getRouter.HandleFunc("/", p.GetProducts)
|
||||
// POST
|
||||
postRouter := r.Methods(http.MethodPost).Subrouter()
|
||||
postRouter.HandleFunc("/", p.AddProduct)
|
||||
postRouter.Use(p.ProductValidationMiddleware)
|
||||
// PUT
|
||||
putRouter := r.Methods(http.MethodPut).Subrouter()
|
||||
putRouter.HandleFunc("/{id:[0-9]+}", p.UpdateProduct)
|
||||
putRouter.Use(p.ProductValidationMiddleware)
|
||||
}
|
||||
23
products/models/product.go
Normal file
23
products/models/product.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Product defines the structure of a product
|
||||
type Product struct {
|
||||
ID int `json:"id"` //TODO: use uuid
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Price float32 `json:"price"` // TODO: use int
|
||||
SKU string `json:"sku"`
|
||||
CreatedOn string `json:"-"`
|
||||
UpdatedOn string `json:"-"`
|
||||
DeletedOn string `json:"-"`
|
||||
}
|
||||
|
||||
// FromJSON read JSON data to create a new product
|
||||
func (p *Product) FromJSON(r io.Reader) error {
|
||||
return json.NewDecoder(r).Decode(p)
|
||||
}
|
||||
Loading…
Reference in a new issue