Monorepo for a simple banking system
This project uses uv for managing dependencies and virtual environments.
-
Install
uvbased on installation guide -
Create and sync the environment:
uv venv # https://github.com/astral-sh/uv/issues/13664 uv sync [--all-packages] -
Running scripts can be done 2 ways
- Using uv
uv run <app.py>OR - Activate virtual environment
uv venvand runpython <app.py>,flask --app hello runetc.
- Using uv
NOTE: Dev dependencies can be
- Installed:
uv sync --group dev - Uninstalled:
uv sync --no-dev
- Project Management with
uv - Dependency Management with
uv - Workspace: Microservices with common dependencies
This project uses Docker to provide the following services:
- Postgres: Database Management System
- pgAdmin: Administration platform for Postgres
- Redis: In-memory key-value database for session management
- Caddy: Reverse Proxy
- step-ca: Certificate Authority for certificate management
Steps to setup docker:
- Install
dockerbased on installation guide for Docker Engine and Docker Compose - Spin up the containers for the above 3 services using
docker compose up -din the project root directory - Check the container status using
docker psand container logs usingdocker logs <container_name>
This project also uses Docker to containerise each of the microservices. Below are some essential info about it:
- Each of the microservices (account, auth, loan, profile & signup) are containerised
- Their
Dockerfilecan be located atservices/<service_name>/ - They utilise the same base image that can be built using the
Dockerfile.baselocated at the project root- Development build has additional private CA baked in so it uses
Dockerfile.dev.base, which adds on to theDockerfile.baseimage - The
entrypoint.shscript for each microservice can be found inscripts/(with or without db versions)
- Development build has additional private CA baked in so it uses
- Their
- All services defined in
compose.yamlwith their appropriate configurations- Development setup has an override configuration
compose.dev.yaml
- Development setup has an override configuration
- Multi-stage build (stepca -> base -> services)
- Makefile commands (Inspect for Docker commands)
- Rebuild stepca, base and services:
make rebuild-all - Rebuild base and services:
make rebuild-base - Rebuild services:
make rebuild-svcs
- Rebuild stepca, base and services:
This project's folder structure is as follows:
banking-system/
├── config: Contains additional configurations used in the project
└── services: Contains all microservices for project
└── <service_name>
├── app: Contains service source code
└── migrations: Contains database change management scripts
This project utilises pre-commit and should be enabled as follows:
- Ensure
uvdev dependencies has been installed (Refer to Python environment) - Enable
pre-commithooks using:pre-commit install - To bypass
pre-commithooks when committing:git commit -m <commit message> --no-verify - To run checks outside of
pre-commithooks:pre-commit run --all-files
This project utilises flask-sqlalchemy (sqlalchemy) and flask-migrate (alembic) to provision the Postgres database.
- When setting up the project environment:
- Ensure that the docker containers are running:
docker psanddocker logs <container_name> - Enter each services directory:
cd services/<service_name>/ - Setup the database schemas and tables:
flask --app app:create_app db upgrade - Login to pgAdmin and verify that the database schemas and tables have been setup:
Servers > Banking DB > Databases > bankingdb > Schemas > SERVICE_NAME
- Ensure that the docker containers are running:
- When updating the service database schema:
- Enter the service directory:
cd services/<service_name>/ - Modify or add your SQLAlchemy models as needed in
app/models.py - Generate a new migration:
flask --app app:create_app db migrate -m "Describe your migration here" - Apply the migration to the database:
flask --app app:create_app db upgrade - Verify the changes in pgAdmin:
Servers > Banking DB > Databases > bankingdb > Schemas > SERVICE_NAME
- Enter the service directory:
This project uses Redis for session management. To see the data inside redis:
apt install redis-tools- If Redis has its port exposed, connect via
redis-cli -h localhost -p 6379 - The following commands can be run
SELECT <db_number>to switch between logical databases in RedisKEYS *(List all the keys)GET <key>(Get a string key)HGETALL <key>(Returns key-value of a key as a flat list)SCAN 0
When creating the Redis session with the Redis database, follow the following format: Redis(host="redis", port=6379, db=0, decode_responses=True), where
host="redis"is the location of your redis serviceport=6379is the port the redis service is listening todb=x, wherexis the database number to connect todecode_responses=Truereturns responses as strings instead as bytes for easier processing
Note that the current implementation has 3 database sessions
db=0for sessID -> dict data mapping. sessID is sent to client upon login. Used to check for a valid session (after login).db=1for userID -> dict data mapping. Request ID in the dict is given to client. Internal use to ensure only one active session.db=2for sessID -> dict data mapping. Similar to db=0, except this is meant for registration workflow for use by 2FA signup.
This project uses Caddy as a reverse proxy. There are currently 2 different configurations for Caddy, 1 for production and 1 for development.
| Configuration | Production | Development |
|---|---|---|
| Caddyfile | Caddyfile | Caddyfile.dev |
| Dockerfile | Dockerfile | Dockerfile.dev |
| Entrypoint | - | entrypoint.sh |
- Production setup will route via
HTTPonly - Development setup will route via
HTTPSonly - If changes were made to
Caddyfile.dev, runmake misc-caddy-reloadto reloadCaddy - Documentation
This project uses Step-CA as a private Certificate Authority. This is currently enabled for development setup only.
- There are 2 core components to this setup
step-ca: Creates the root and intermediate certificates to issue and sign certificatesstep-cli: Communicate with the private CA to provision the certificate and private key
- You will need to setup 3 password files before running the private CA for the first time (Run
make misc-stepca-init) - Next, you will need to setup the full build stages (Run
make rebuild-all. See details on Makefile commands)- You will probably need to run
make rebuild-allonce only - Subsequently, running
make rebuild-baseormake rebuild-svcswill suffice - Running
make rebuild-allwill reinitialise a new CA which will then reinstall to the base image
- You will probably need to run
- Provisioning of the service certificate and private key
- Additional script
scripts/stepca-provision.shhas been included inside each service image - It will provision the service certificate and private key
- An auto-renewal daemon has been setup as well to run automatically
- When renewal is complete, it will kill the existing
Gunicornprocess so it will start with the new certificate and private key
- Additional script
- Documention:
This project uses pytest for unit tests. Below are the recommended steps to run tests and troubleshoot common issues when using uv to manage the Python environment.
-
Ensure the virtual environment exists and dependencies are installed:
uv sync --all-packages -
Run tests using
uv(preferred) or by activating the venv manually.
# run the whole test suite
uv run pytest
# run only the signup tests (example)
uv run pytest tests/signup/test_signup.py -q
# or activate the venv and run directly
uv venv
source .venv/bin/activate
pytest -q tests/signup/test_signup.pyThis project includes a small seeder utility to generate fake data and Alembic-style seed files for each service.
Basic workflow :
- Generate a seed
python3 seeder/seed_generator.py --service auth --name auth- Apply the seed to the database. The Makefile target will invoke the Flask CLI seed command for the chosen service.
# using Makefile (recommended)
make seed SERVICE=authNotes
- The auth faker factory creates HR users by default. The seeded HR account has the a default password
- The default password can be found or changed at
seeder/faker_factories/auth.py - Seeding will modify the target schema in your Postgres instance. Only run against a development database. (ensure it is running before seeding)