This is a full-stack, cloud-native web application for tracking and splitting expenses and settling debts among 2 individuals. It is built on PHP 8.4, Symfony 8, MySQL 8, and React 19, and uses event sourcing to maintain a complete audit trail of all financial transactions.
It follows DDD principles to separate the application into generic, core, and supporting domains โ enforced by deptrac. This makes it easy to achieve full code coverage for the core domain.
It also showcases Kubernetes deployment using Helm.
![]() |
![]() |
![]() |
- Make
- Docker
- Kubernetes (kind, minikube, or Docker Desktop)
- Helm 3
make start # Build images, start services, and open in browser
make help # Show all available targetsVisit http://localhost:8000 in your browser.
Browser / Client
โ
โโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโ
โ :8000 โ :8080 โ :5173 (dev)
โผ โผ โผ
โโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ
โ dashboard โ โ web (Nginx) โ โ npm-dev โ
โ (Homer) โ โ โ โ (Vite/React/ โ
โโโโโโโโโโโโโโ โโโโโโโโฌโโโโโโโโ โ TypeScript) โ
โ FastCGI โโโโโโโโโโโโโโโโโ
โ :9000
โผ
โโโโโโโโโโโโโโโโโโโโ async โโโโโโโโโโโโโโโโ
โ app (PHP-FPM) โโโโโmessagesโโโโโถโ worker โ
โ Symfony โ โ (Messenger) โ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ โ
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ SQL
โผ
โโโโโโโโโโโโโโโโโโโ
โ db (MySQL) โ
โโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโ Backend Layers โโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Supporting โ Controllers ยท Auth ยท Repositories ยท Async โ
โ โ Normalizers ยท Instrumentation ยท EventListeners โ
โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Core โ ExpenseTracker ยท Calculator โ
โ (Domain) โ Event Sourcing ยท Expenses ยท Compensation โ
โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Generic โ Symfony ยท Doctrine ยท Twig ยท DomPDF ยท Monolog |
| โ PhpParser ยท phpDocumenter ยท OpenAPI |
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Supporting depends on Core & Generic ยท Core has no deps
# Build production images
make prod
# Deploy with 3 app pods + 2 worker pods
helm install app ./helm --set replicaCount.app=3 --set replicaCount.worker=2
# or:
helm upgrade --install app ./helm
# Access the application via NodePort:
# Link: http://localhost:30190
# Or configure port forwarding via:
kubectl port-forward svc/app-split-fairly-web 8080:80
# Link: http://localhost:8080
# View logs for all pods with the PHP label (app + worker)
kubectl logs -f -l technology=phpโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Kubernetes Cluster โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ nginx (web) โ โ app (PHP-FPM) โ โ worker โ โ
โ โ Deployment โ โ Deployment โ โ (Messenger) โ โ
โ โ NodePort:30190 โ โ Pod ร 1 โ โ Pod ร 1 โ โ
โ โ Pods ร 1 โโโโ FastCGI :9000 โ โ One-shot pattern โ โ
โ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ โ
โ โ Serves SPA โ Business logic โ Async tasks โ
โ โ EasyAdmin โ API endpoints โ from queue โ
โ โ Static assets โ Session mgmt (DB) โ โ
โ โ โ Event sourcing (DB) โ โ
โ โ โ โ โ
โ โโโโโโโโโโโโโฌโโโโโโโโโโโโดโโโโโโโโโโโโฌโโโโโโโโโโโโ โ
โ โ โ โ
โ โโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโผโโโ โ
โ โ MySQL StatefulSet โ โ
โ โ PVC Storage (8Gi) โ โ
โ โ - Event store โ โ
โ โ - Sessions โ โ
โ โ - Application data โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โณ โ
โ โ init Job โ
โ โโโโโโโโดโโโโโโโ โ
โ โ db-init โ โ
โ โ (one-time) โ โ
โ โโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Grafana Alloy (k8s-monitoring) โ โ
โ โ - Collects logs from all pods โ โ
โ โ - Metrics collection & forwarding โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Components:
- nginx (web): Serves React SPA frontend, static assets, EasyAdmin UI. Proxies API requests to PHP-FPM via FastCGI.
- PHP-FPM (app): Symfony backend handling business logic, API endpoints, and session management. Stores sessions in MySQL.
- Worker: Processes async jobs via Messenger with one-shot pattern (processes single message per pod lifecycle, then restarts for fresh environment).
- MySQL: Persistent data storage with StatefulSet and PVC. Stores event sourcing audit trail, sessions, and application data.
- db-init Job: One-time database initialization (schema, fixtures, migrations).
- Grafana Alloy: Log collection and forwarding for observability across all cluster components.


