sample-service

module
v0.0.0-...-cb069d3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2022 License: MIT

README

Sample-service

Golang CRUD service for DVD store that communicates by GRPC and follows the principles of Clean Architecture by Robert Martin.

It has simplified business logic in order to concentrate on architecture, code organization and practicing GRPC.

Project structure

Project structure (mostly) follows Standard Go Project Layout.

  • /cmd - entry point of the app
  • /config - configuration
  • /internal/dvdstore - application code (interfaces, transports, implementations)
  • /internal/dvdstore/grpc - GRPC transport
  • /internal/dvdstore/repository - working with repositories, currently only postgresql
  • /internal/dvdstore/usecase - business logic
  • /internal/models - entities, exported errors, custom validations
  • /internal/server - initialization of the app ("continues" main.go)
  • /pkg/postgres - postgres connection config
  • /proto - protobuf definition and proto-generated code

To follow dependency inversion, use cases and repositories are described through interfaces.
Concrete repository implementations realize communication with needed data sources, in this project it is postgresql.
Concrete use case implementations aggregate repository interface; transport (grpc) aggregates use case interface.
Such code organization simplifies unit testing and allows us to make code flexible - we can easily add/switch between data sources and transports, write different use cases.

Request processing logic

Transport receives request from client and calls use case. Use case validates the request and calls repository. Repository retrieves data from data source and forms entity. Entity is mapped to response structure and returned to client with status code. If any error appears, the app returns corresponding error with error code.

DB schema

Project uses Dell DVD store database:

DB schema

Some of the tables are ignored to simplify the business logic.

Running and usage

# Build and start container with database
docker compose up -d

# Load dependencies
go mod tidy

# Run the app
go run cmd/main.go

If everything is ok, you will see this message:

{"level":"info","msg":"GRPC listening on port 9090"}
Usage

You can use any preferred GRPC client to call API, for example grpcurl or Postman.
Service uses reflection, so you can describe it through the client.

# describe service
grpcurl -plaintext localhost:9090 describe

# describe message 
grpcurl -plaintext localhost:9090 describe proto.GetCustomerReq

# request customer orders
grpcurl -d '{"CustomerID": 268}' -plaintext localhost:9090 proto.Dvdstore/GetCustomerOrders

Though I recommend to use Postman.

API methods

Customers
GetCustomers

GetCustomers returns list of all Customers limited by provided limit

Request Response
{
    "Limit": 2
}
{
    "CustomerList": [
        {
            "Id": "2",
            "FirstName": "HQNMZH",
            "LastName": "UNUKXHJVXB",
            "Age": "80"
        },
        {
            "Id": "3",
            "FirstName": "JTNRNB",
            "LastName": "LYYSHTQJRE",
            "Age": "47"
        }
    ]
}
GetCustomer

GetCustomer returns Customer by provided id

Request Response
{
    "CustomerID": 268
}
{
    "Customer": {
        "Id": "268",
        "FirstName": "MKZPVX",
        "LastName": "CBIHNABLQI",
        "Age": "54"
    }
}
AddCustomer

AddCustomer adds passed Customer and returns his id. Passed customer "Id" field is ignored

Request Response
{
    "Customer": {
        "Age": 30,
        "FirstName": "John",
        "LastName": "Doe"
    }
}
{
    "CustomerID": "20003"
}
DeleteCustomer

DeleteCustomer deletes Customer by provided id. Returns empty response if no errors were met

Request Response
{
    "CustomerID": 16
}
{}
Products
GetProducts

GetProducts returns list of all Products limited by provided limit

Request Response
{
    "Limit": 2
}
{
    "ProductList": [
        {
            "Id": "1",
            "Title": "ACADEMY ACADEMY",
            "Price": 25.99,
            "Quantity": "138"
        },
        {
            "Id": "2",
            "Title": "ACADEMY ACE",
            "Price": 20.99,
            "Quantity": "118"
        }
    ]
}
GetProduct

GetProduct returns Product by provided id

Request Response
{
    "ProductID": 17
}
{
    "Product": {
        "Id": "17",
        "Title": "ACADEMY ALONE",
        "Price": 28.99,
        "Quantity": "114"
    }
}
AddProduct

AddProduct adds passed Product and returns his id. Passed product "Id" field is ignored

Request Response
{
    "Product": {
        "Price": 10.55,
        "Quantity": 6,
        "Title": "Product"
    }
}
{
    "ProductID": "10006"
}
DeleteProduct

DeleteProduct deletes Product by provided id. Returns empty response if no errors were met

Request Response
{
    "ProductID": 58
}
{}
Orders
GetOrder

GetOrder gets order by provided id

Request Response
{
    "OrderID": 54
}
{
    "Order": {
        "Id": "54",
        "Date": {
            "seconds": "1074124800"
        },
        "NetAmount": 311.01,
        "Tax": 25.66,
        "TotalAmount": 336.67,
        "ProductList": [
            {
                "Id": "5787",
                "Title": "AGENT SHINING",
                "Price": 9.99,
                "Quantity": "3"
            }
        ]
    }
}
GetCustomerOrders

GetCustomerOrders returns customer orders by provided customer id

Request Response
{
    "CustomerID": 359
}
{
    "OrderList": [
        {
            "Id": "7453",
            "Date": {
                "seconds": "1091836800"
            },
            "NetAmount": 124.11,
            "Tax": 10.24,
            "TotalAmount": 134.35,
            "ProductList": [
                {
                    "Id": "7114",
                    "Title": "AIRPORT CAMELOT",
                    "Price": 9.99,
                    "Quantity": "3"
                }
            ]
        }
    ]
}
AddOrder

AddOrder adds order for passed customer id with provided products and returns created order id.
"Title" and "Price" fields in passed ProductList are ignored

Request Response
{
    "CustomerID": 36,
    "ProductList": [
        {
            "Id": 34,
            "Quantity": 2
        },
         {
            "Id": 92,
            "Quantity": 10
        }
    ]
}
{
    "OrderID": "12010"
}
DeleteOrder

DeleteOrder deletes order with provided order id. Returns empty response if no errors were met

Request Response
{
    "OrderID": 14
}
{}

Directories

Path Synopsis
internal
pkg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL