Independent task: wallet service on FastAPI and SQLAlchemy
Create a new public repository or a separate project folder. The task is completed from scratch and does not depend on branches or the results of previous homework.
Service context
The service is responsible only for the wallet domain: storing the balance and transfers between wallets. The user is represented by an external user_uid: UUID; there is no user entity of its own. Authentication and authorization are not covered in this tutorial. Locally, the service runs as a standalone application. For production, a trust model, protection against gateway bypass, and verification of operation rights would be separately required.
Minimal environment: Python, FastAPI, SQLAlchemy 2.x, Alembic, PostgreSQL, pytest and Docker Compose. Instructions for installation, running migrations and tests should be in the README.
Tasks:
- SQLAlchemy.
Work out the architecture of money transfer service models. You need to create a wallet entity and a “transaction”/transfer entity. Wallets are tied to users (user_uid: UUID, uuid from another system).
Work through implementation limitations.
What we use:
- Lecture :)
- SQLAlchemy models
https://docs.sqlalchemy.org/en/20/orm/quickstart.html#declare-models - Alembic migrations.
https://alembic.sqlalchemy.org/en/latest/tutorial.html(doc) What to do:- Make a list of possible questions and suggestions for solutions to problems
- For example: “Can the wallet balance go negative?” - "No". Then we consider restrictions at the database level and correct processing of competitive operations.
- All unclear things should be clarified with us (we are training to formulate requirements)
- Implement entities and migration.
- Consider what indexes and constraints are needed.
- Add Alembic migration.
- FastAPI handlers. Implement handles for obtaining the wallet balance (separately by user_uid, separately by wallet_id). Implement a handle for transferring money from one wallet to another
- Implement handles
GET /wallet/{id}— getting the wallet balance. Consider validation; if the wallet is missing, return it404 Not Found.- GET /users/{user_uid}/wallet
POSTorPUT /wallet/transaction— justify the choice of method, resource URI and idempotency model.- Cover all scenarios with tests
- Think about and implement idempotent money transfer
- Think about and implement competitive money transfer Useful to learn:
- Sergey's original course on FastAPI
- Handle testing
- Testing - FastAPI
- Row locks and isolation levels for concurrent changes
- Lecture :)
- PostgreSQL: explicit locking
- SQLAlchemy: SELECT … FOR UPDATE Additional tasks:
- Add Redis to Docker Compose.
- Add caching of the GET operation for obtaining a balance and consider invalidating the cache during translation.
Examples of services
An example of implementing a simple service with handles:
- GitHub - takentui/backend: Simple backend example
Readiness criteria
- a project is launched from scratch using the README and PostgreSQL is raised via Docker Compose;
- Alembic applies migrations to an empty database;
- wallet and transfer models contain reasonable types, constraints, indexes and connections;
- API returns correct
2xx,404and409/422for selected error scenarios; - the transfer is performed in one transaction and does not allow a negative balance for competitive requests;
- repeating the request with the same idempotency key does not create a second translation;
- tests check for successful transfer, insufficient funds, missing wallet, idempotency key repeat and competitive debit;
- secrets and local
.envare not in the repository; safe present.env.example; - the additional Redis cache, if implemented, is not a source of truth and is invalidated after translation.