A tiny, dependency-free stock portfolio domain model in Python, used as a live
example in a Continuous Integration training session. No web framework, no
database, no external services — just business rules and the tests that pin
them down, wired to a GitHub Actions pipeline that blocks merging into main
when a test fails.
portfolio_domain/
money.py Money value object (immutable, rounds to 2 decimals)
portfolio.py Portfolio aggregate: deposit / buy / sell shares, FIFO lots
exceptions.py Domain errors (insufficient funds / insufficient shares)
tests/
test_money.py
test_portfolio.py
.github/workflows/ci.yml Runs the full test suite on every push and PR
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
pytest -vAll 15 tests run in well under a second — no Docker, no network, no database.
main is protected: a pull request cannot be merged while the
Run domain tests check is failing. Try it yourself:
- Create a branch.
- Break something in
portfolio_domain/portfolio.py(or "fix" a test to expect the wrong thing). - Push the branch and open a pull request against
main. - Watch the check turn red — the Merge button is disabled.
- Fix the code, push again — the check turns green and the PR becomes mergeable.
- DEMO_SCRIPT.md — condensed, copy-paste-ready run sheet for the live walkthrough above.
- docs/WALKTHROUGH.md — the same walkthrough fully explained, with diagrams, plus a follow-up exercise to repeat the whole thing on your own fork afterwards.
MIT — see LICENSE.