wallet

package module
v0.0.0-...-c801cc5 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2019 License: MIT Imports: 16 Imported by: 0

README

Tiny Wallet

Tiny Wallet

A simplistic and minimalistic distributed wallet microservice

Go Report Card GoDoc Build Status Heroku Coverage Status License

Overview

Tiny Wallet is a service that allows you to process any payments between simple financial accounts.

Contents

About Tiny Wallet

Tiny Wallet helps you to serve simple payment between accounts. As a internal microservice (without direct access to outside) it hasn't authentication capabilities, but they can be easily implemented as a go-kit middleware.

The service is thread-safe and lock-free scalable application, so it can run multiple replicas over any load balancer without concurrent problems.

As a cloud-native application it can run on any cloud platform (if it doesn't support Go, you can just build it before deploy, as Go supports cross-compilation), but also Docker or K8s.

Requirements

There is a list or application requirements for different deployment scenario:

  • local run:
    • Go 1.11.x or greater. Not compatible with earlier versions of Go from 1.10 because of mod usage;
    • Go mod should be enabled;
    • PosgreSQL (no specific version, it has to support serializable transaction isolation level). You don't have to install the database on your PC, you can also use dockerized or cloud PostgreSQL;
  • Docker Compose:
    • Docker;
    • Docker Compose;
  • cloud deployment.
    • Go 1.12.x on the platform;
    • Cloud PostgreSQL;
  • cloud deployment with Docker:
    • Docker support;
    • Cloud PostgreSQL;

Usage

Here is a list of possible ways to run the service.

To get a list of CLI flags and environment variables run from the project root directory:

go run cmd/tiny-wallet/wallet.go -h
Download

You can get the app with go get:

go get github.com/ilyakaznacheev/tiny-wallet

or clone the repo into any directory outside $GOPATH:

git clone https://github.com/ilyakaznacheev/tiny-wallet.git
Run Local

Prerequirements

To run the service locally, you need to have a running PostgreSQL database.

It can be local, dockerized or even cloud database, you only need to provide its connection information to the app.

To set up the database itself, please apply .sql files from migrations directory in alphanumerical order. This action will create the schema required for the application to run.

Starting the service

Go to the project root directory and run the app by executing the following command:

make run

or by means of go if your operating system doesn't support make:

go run cmd/tiny-wallet/wallet.go

Build

You can also build an executable file to run it. Call

make build

or

go build cmd/tiny-wallet/wallet.go
Docker Compose

You can run the whole app infrastructure in the Docker Compose. See Requirements for this case.

To run the app call from the project root directory:

docker-compose up

This will start the service and the database and bind the app to port 8080.

To stop the app run

docker-compose down
Deployment

The service in containerized with Docker, so you can use the deployments/docker/wallet/Dockerfile to deploy it to any service that supports Docker containers, e.g. GKE, AWS, Heroku, etc.

Heroku

This service is already configured to be deployed on Heroku. You only need to push the repo to your own Heroku app or deploy it another possible way.

Note: while deploying on Heroku specify environment variable HEROKU=X in the Heroku Dashboard. This will allow the app to run some Heroku-specific start-up logic.

Online

You can try the service online at tiny-wallet.herokuapp.com/api

API documentation

Service public API is documented in plain text and swagger. Try it in Swagger Editor!

Testing

The business logic of the app and internal libraries are covered with unit-tests. The generated code or simple technical code (like one-liner that call another function) are not covered with tests now.

To run tests on your PC run

make test

Contributing

The application is open-sourced under the MIT license.

If you will find some error, want to add something or ask a question - feel free to create an issue and/or make a pull request.

Any contribution is welcome.

Documentation

Overview

Package wallet is a simplistic and minimalistic distributed wallet microservice

Tiny Wallet is a service that allows you to process any payments between simple financial accounts.

Tiny Wallet helps you to serve simple payment between accounts. As a internal microservice (without direct access to outside) it hasn't authentication capabilities, but they can be easily implemented as a go-kit middleware.

The service is thread-safe and lock-free scalable application, so it can run multiple replicas over any load balancer without concurrent problems.

As a cloud-native application it can run on any cloud platform (if it doesn't support Go, you can just build it before deploy, as Go supports cross-compilation), but also Docker or K8s.

Overview

The project is based on go-kit (https://github.com/go-kit/kit). Thus, the application consists of several main levels combining together with interfaces. There are several levels, implemented in this app:

- transport: HTTP routing and the way the app communicate with the outer world: transport.go (https://github.com/ilyakaznacheev/tiny-wallet/blob/master/transport.go)

- endpoint: the implementation of each route handling that calls underlying business logic: endpoints.go (https://github.com/ilyakaznacheev/tiny-wallet/blob/master/endpoints.go)

- service: main business logic implementation: service.go (https://github.com/ilyakaznacheev/tiny-wallet/blob/master/service.go)

- database: communication with database and transaction control: internal/database/postgres.go (https://github.com/ilyakaznacheev/tiny-wallet/blob/master/internal/database/postgres.go)

Running the app

To run the service call the entry-point CLI app

cmd/tiny-wallet/wallet.go

You can just run it with

go run cmd/tiny-wallet/wallet.go

or compile into an executable with

go build cmd/tiny-wallet/wallet.go

Usage and help

To get help run the app with -h flag. You will get a list of command-line arguments and a list of used environment variables.

go run cmd/tiny-wallet/wallet.go -h

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func MakeHTTPHandler

func MakeHTTPHandler(s Service, logger log.Logger) http.Handler

MakeHTTPHandler mounts all of the service endpoints into an http.Handler

Types

type Account

type Account struct {
	ID       string            `json:"id"`
	Balance  float64           `json:"balance"`
	Currency currency.Currency `json:"currency"`
}

Account is a financial account.

It is used to structure REST response data.

type Database

type Database interface {
	GetAllAccounts() ([]model.Account, error)
	GetAllPayments() ([]model.Payment, error)
	GetAccount(accountID string) (*model.Account, error)
	CreatePayment(p model.Payment, lastChangedFrom, lastChangedTo *time.Time) (*model.Payment, error)
	CreateAccount(a model.Account) (*model.Account, error)
}

Database is a common interface for a database layer

type Endpoints

type Endpoints struct {
	// GetAllPaymentsEndpoint returns all payments in the system
	GetAllPaymentsEndpoint endpoint.Endpoint
	// GetAllAccountsEndpoint returns all accounts in the system
	GetAllAccountsEndpoint endpoint.Endpoint
	// PostPayment processes a new payment
	PostPayment endpoint.Endpoint
	// PostAccount creates a new account
	PostAccount endpoint.Endpoint
	// RedirectMain redirects the user from the main page
	RedirectMain endpoint.Endpoint
	// RedirectAPI redirects the user from the API page
	RedirectAPI endpoint.Endpoint
}

Endpoints is a set of service API endpoints

func MakeServerEndpoints

func MakeServerEndpoints(s Service) Endpoints

MakeServerEndpoints creates server handlers for each endpoint

type ErrHTTPStatus

type ErrHTTPStatus struct {
	// contains filtered or unexported fields
}

ErrHTTPStatus is an error with HTTP status and error description

func NewErrHTTPStatusf

func NewErrHTTPStatusf(code int, err error, format string, a ...interface{}) *ErrHTTPStatus

NewErrHTTPStatusf creates a new HTTP error based on HTTP code and formatted string code: HTTP status code err: underlying error to wrap in format: string formatting pattern a: formatting attributes

func (ErrHTTPStatus) Code

func (e ErrHTTPStatus) Code() int

Code returns a HTTP status code of the error

func (ErrHTTPStatus) Error

func (e ErrHTTPStatus) Error() string

Error returns an text description of the error

func (ErrHTTPStatus) Unwrap

func (e ErrHTTPStatus) Unwrap() error

Unwrap returns wrapped error

type ErrorResponse

type ErrorResponse struct {
	Code  int                  `json:"code"`
	Error ErrorResponseMessage `json:"error"`
}

ErrorResponse is a JSON error response structure

type ErrorResponseMessage

type ErrorResponseMessage struct {
	Text    string   `json:"text"`
	Details []string `json:"details,omitempty"`
}

ErrorResponseMessage is an error message and details

type GetAllAccountsResponse

type GetAllAccountsResponse struct {
	Accounts []Account `json:"accounts"`
}

GetAllAccountsResponse is a request structure for the GetAllAccounts endpoint.

It is used to structure REST response data.

type GetAllPaymentsResponse

type GetAllPaymentsResponse struct {
	Payments []Payment `json:"payments"`
}

GetAllPaymentsResponse is a request structure for the GetAllPayments endpoint

It is used to structure REST response data.

type HTTPError

type HTTPError interface {
	error
	Code() int
}

HTTPError is an error with an HTTP status code

type Payment

type Payment struct {
	AccFromID string            `json:"account-from"`
	AccToID   string            `json:"account-to"`
	DateTime  time.Time         `json:"time,omitempty"`
	Amount    float64           `json:"amount"`
	Currency  currency.Currency `json:"currency"`
}

Payment is a financial transaction between accounts.

It is used to structure REST response data.

type PostAccountRequest

type PostAccountRequest struct {
	ID       string  `json:"id"`
	Balance  float64 `json:"balance"`
	Currency string  `json:"currency"`
}

PostAccountRequest is a request structure for the PostAccount endpoint.

It is used to structure REST request data.

type PostPaymentRequest

type PostPaymentRequest struct {
	AccountFromID string  `json:"account-from"`
	AccountToID   string  `json:"account-to"`
	Amount        float64 `json:"amount"`
}

PostPaymentRequest is a request structure for the PostPayment endpoint.

It is used to structure REST request data.

type Service

type Service interface {
	GetAllPayments(ctx context.Context) ([]model.Payment, error)
	GetAllAccounts(ctx context.Context) ([]model.Account, error)
	PostPayment(ctx context.Context, from, to string, amount float64) (*model.Payment, error)
	PostAccount(ctx context.Context, id string, balance float64, curr string) (*model.Account, error)
}

Service is a set of CRUD operations that the backend can process

func NewWalletService

func NewWalletService(db Database) Service

NewWalletService creates a new wallet service with a connection to the database

type WalletService

type WalletService struct {
	// contains filtered or unexported fields
}

WalletService is a business logic implementation of a Tiny Wallet.

It is responsible to process HTTP requests and manipulate the data of accounts and payments between them.

func (*WalletService) GetAllAccounts

func (s *WalletService) GetAllAccounts(ctx context.Context) ([]model.Account, error)

GetAllAccounts returns a list of all accounts in the system

func (*WalletService) GetAllPayments

func (s *WalletService) GetAllPayments(ctx context.Context) ([]model.Payment, error)

GetAllPayments returns a list of all payments in the system

func (*WalletService) PostAccount

func (s *WalletService) PostAccount(ctx context.Context, id string, balance float64, curr string) (*model.Account, error)

PostAccount creates a new financial account.

If the account already exists, it will return 409 Status Code

func (*WalletService) PostPayment

func (s *WalletService) PostPayment(ctx context.Context, fromID, toID string, amount float64) (*model.Payment, error)

PostPayment processes a financial transaction between two accounts.

The method is thread-safe and allows serialized access to payment changes.

It is lock-free, so it gives a good performance in distributed systems and allows you to read the data very fast and write without concurrency issues.

The method is based on compare-and-swap(https://en.wikipedia.org/wiki/Compare-and-swap) pattern.

Thus, the method reads the current state of both payer and receiver accounts. That allows it doesn't hold the database transaction open while the app processes the business logic, which can take a long time. After that, if there is all business checks are good, the application creates a serialized database transaction, that tries to update account state and save the payment. If the account state was changed meanwhile (i.e. another payment had affected any of these accounts), the transaction will fail. The serialized transaction will not allow concurrent process to create a payments during this update without database lock. That gives a good performance and thread-safety.

Directories

Path Synopsis
cmd
internal
config
Package config contains application configuration structures and data read logic.
Package config contains application configuration structures and data read logic.
database
Package database contains Database interface implementation for certain database.
Package database contains Database interface implementation for certain database.
model
Package model contains common application models and types
Package model contains common application models and types
pkg
currency
Package currency contains ISO 4217 currency codes and currency names.
Package currency contains ISO 4217 currency codes and currency names.

Jump to

Keyboard shortcuts

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