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 ¶
- func MakeHTTPHandler(s Service, logger log.Logger) http.Handler
- type Account
- type Database
- type Endpoints
- type ErrHTTPStatus
- type ErrorResponse
- type ErrorResponseMessage
- type GetAllAccountsResponse
- type GetAllPaymentsResponse
- type HTTPError
- type Payment
- type PostAccountRequest
- type PostPaymentRequest
- type Service
- type WalletService
- func (s *WalletService) GetAllAccounts(ctx context.Context) ([]model.Account, error)
- func (s *WalletService) GetAllPayments(ctx context.Context) ([]model.Payment, error)
- func (s *WalletService) PostAccount(ctx context.Context, id string, balance float64, curr string) (*model.Account, error)
- func (s *WalletService) PostPayment(ctx context.Context, fromID, toID string, amount float64) (*model.Payment, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
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 ¶
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
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 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 ¶
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 ¶
GetAllAccounts returns a list of all accounts in the system
func (*WalletService) GetAllPayments ¶
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. |