/zivwi

Drive slow, homie.

Primary LanguageGo

zivwi

Drive slow, homie.

This repository contains a payment handling backend system prototype built in Go. Using NSQ, it asynchronously processes transactions.

Context

STACK: Echo + NSQ + Postgres

As stated above, this prototype has one goal: to allow transfers between accounts and the retrieval of their balances. To accomplish that, the following data model has been proposed:

  • User: email, password, first and last name. Many accounts.
  • Account: it just has a balance (expressed in cents) which can't go below zero.
  • Transfer: it stores the amount (expressed in cents), the source account and the destination account, as well as its status and timestamps (created, updated). Additionally, it counts with two optional fields: transaction concept and one to log error messages in case that the transaction can't be fulfilled.

Once presented the data model, let dive deep on the fundamentals and foundations of this solution.

First of all, while coding this prototype, my technical inspiration was highly driven by two design patterns: Event Sourcing and CQRS, which are often related one to another. In the proposed solution, these patterns have been superficially implemented. For instance, the app logs events in the flavor of "Transfers". Then, the internal services calculate in a background process (a primitive version of a CQRS Saga, having the account balances calculated on the existing Transfer records stored in the database.

By being inspired by these patterns and embracing eventual consistency, I've created a system that dodges race-conditions. It also eases out audition efforts and reduces response times, among other goodies.

In another line of decisions, I have used relational schema black arts to accomplish my goals, rather than keeping a pristine schema that does a better job on maintaining the referential integrity across tables. For instance, I decided that the "users" table would hold an array of account identifiers, allowing for faster logins by querying just once the database (without joining tables).

Setup

These are the following steps to get up and running. Before following these instructions, make sure that the ports 3000, 4150, 4151, 4160, 4161 and 4171 are not in usage.

  1. clone this repo somewhere in your $GOPATH.
  2. Move into the projects directory.
  3. docker-compose up

Usage

PROTIP: If you're a user of Paw, I left a Paw project in the repository for trying out the API.

Once set up, perform the following requests:

  1. Grab the JWT token
$ curl -X "POST" "http://localhost:3000/authorize" \
     -H 'Content-Type: application/json' \
     -d $'{
  "email": "jefe@inditex.es",
  "password": "dameDiner0"
}'
{ "token": "TOKEN" }
  1. Request for your accounts (with the following user you'll find two) and grab their respective ids.
$ curl "http://localhost:3000/q/accounts" \
     -H 'Authorization: Bearer <JWT>'
{
  "accounts": [
    {
      "id": "UUID1",
      "balance": 12000000000
    },
    {
      "id": "UUID2",
      "balance": 5600000000000000
    }
  ]
}
  1. 💸
$ curl -X "POST" "http://localhost:3000/cmd/transfer" \
     -H 'Authorization: Bearer <TOKEN>' \
     -H 'Content-Type: application/json' \
     -d $'{
  "amount": 1,
  "message": "Te quiero",
  "from_account_id": "UUID1",
  "to_account_id": "UUID2"
}'
  1. Repeat step two, and you should see a change.

TODO's & Room for Improvement

Here, the list of improvements I would have taken on with more time:

  • Add support for currency and fully implement the Money pattern.
  • Optimize size of Docker images.
  • Added i18n support to the error messages generated by the backend (or at least to return error codes that can be easily translated in the frontend).
  • Implement GraphQL for mutations, query, payload validation and subscriptions too.
  • Add application monitoring and issue application metrics.
  • Add CI/CD integration.
  • Add more code comments.