ExpenShare
Personal finance and shared expenses without collapsing wallet history and interpersonal debt into the same ledger.
Context
Live in production and under continuous active development, ExpenShare combines account-aware personal finance with shared-group cost allocation and settlement workflows. Canonical transactions and participant splits feed projected pair balances, settlements reduce those balances through explicit allocations, and each participant controls whether settlement activity is also recorded in their own wallet history.
Personal money tracking and shared-expense fairness are related, but they do not represent the same state. A useful system must explain both where money moved and who owes whom without allowing one view to silently rewrite the other.
ExpenShare treats wallet activity, shared fairness, and settlement workflow as explicit connected domains. This supports personal-only use while retaining deeper group workflows when costs are shared between people.
- Preserve a clear audit trail for financial inputs, allocations, corrections, and reversals.
- Keep participant-owned wallet decisions separate from shared fairness state.
- Enforce access across personal accounts, shared accounts, groups, and settlement participants.
- Preserve native amounts and explicit conversion data across multi-currency flows.
- Keep development, staging, and production behavior aligned across the web app, API, and workers.
Responsibilities
Kevin independently designed and implemented the current software system, including its backend, frontend, domain model, integrations, infrastructure, and deployment workflow.
Product Direction
Product requirements, system behavior, implementation direction, and continued development.
Application Architecture
Django REST backend, React frontend, background workers, and their runtime boundaries.
Domain Model
Wallet activity, shared fairness, settlements, groups, lending, recurrence, and foreign exchange.
Infrastructure And Delivery
Containerized environments, service releases, observability, and backup workflows.
Collaboration credit
- Product collaboratorJointly created the ExpenShare concept and continues to provide substantial improvement feedback.
- Additional reviewersProvided smaller but meaningful product feedback.
Architecture
System context
06 boundaries · 07 declared relationships
- 01React web clientClient
- 02Django REST APIAPI
- 03PostgreSQLDatastore
- 04Recurring, push, and FX workersWorker
- 05Identity, email, FX, push, and backup providersExternal Provider
- 06Centralized logs and dashboardsMonitoring
Relationship mapInspect 07 connectionsOpen
- React web clientRequestDjango REST API
- Django REST APIPersistencePostgreSQL
- Recurring, push, and FX workersPersistencePostgreSQL
- Django REST APIIntegrationIdentity, email, FX, push, and backup providers
- Recurring, push, and FX workersIntegrationIdentity, email, FX, push, and backup providers
- Django REST APIMonitoringCentralized logs and dashboards
- Recurring, push, and FX workersMonitoringCentralized logs and dashboards
Engineering decisions
01Separate wallet history from shared fairnessInspect
Context
Account balances and interpersonal obligations are related but have different ownership and effects.
Selected approach
Model them as separate systems connected through explicit transaction and settlement workflows.
Rationale
Prevent one participant's wallet choice from silently changing shared debt, and preserve an explainable audit trail.
Trade-offs
- More domain models and workflow states must be coordinated.
- The interface must clearly explain wallet balance versus shared balance.
02Project current debt from canonical inputsInspect
Context
Transactions, splits, settlements, and corrections all contribute to current obligations.
Selected approach
Rebuild persisted pair-balance read models from canonical source records.
Rationale
Keep current debt consistent and efficient to read without discarding the inputs that explain it.
Trade-offs
- Projection rebuild and reconciliation paths must be maintained.
- Derived balances must be protected from direct edits.
03Reverse confirmed actions through compensationInspect
Context
Confirmed settlements affect audited allocations and may also have participant-owned wallet records.
Selected approach
Preserve confirmed history and create compensating adjustments when a settlement is reversed.
Rationale
Restore projected balances without rewriting the financial record in place.
Trade-offs
- Reversal introduces additional records and states.
- Wallet reversals remain participant-specific decisions.
04Separate worker releases without duplicating the runtime stackInspect
Context
Recurring, push, and FX processes use the same application dependencies but can change independently.
Selected approach
Publish separate service tags from one shared backend image definition.
Rationale
Support independent rollout while the runtime dependencies remain common.
Trade-offs
- Worker-specific image definitions will be needed if their dependencies diverge.
05Upload a new backup before enforcing retentionInspect
Context
Automated database backups are retained in object storage.
Selected approach
Create and upload a fresh dump before pruning expired backups.
Rationale
A failed backup cycle must not reduce the existing recovery set.
Trade-offs
- Expired backups remain temporarily when a new upload fails.
Runtime ownership
- 01Containerization
Docker Compose separates development, staging, and production.
- 02Reverse Proxy
Nginx routes the public application and API and serves the built client.
- 03Storage
PostgreSQL and environment-specific application file storage are persistent.
Inspect operational detail
- PostgreSQL persistent volumes
- Environment-specific media and static-file storage
- 04Backups
Scheduled PostgreSQL dumps are uploaded to S3-compatible storage.
Inspect operational detail
- Retention pruning runs only after a successful new upload.
- 05Monitoring
Loki, Promtail, and Grafana provide centralized log exploration.
- 06Logging
Production services emit structured, request-correlated lifecycle and failure logs.
- 07Scheduled Tasks
Dedicated processes generate recurrence, reminders, exchange-rate updates, and backups.
- 08Background Jobs
Recurring, push-delivery, and foreign-exchange workers run independently from the API.
- 09Notifications
In-app and Web Push channels deliver product notifications.
- 10Email
Environment-configured SMTP supports mandatory account verification.
- 11Deployment
Service-specific release tags support independent application and worker rollout.
Inspect operational detail
- Backend and workers share one aligned runtime definition.
- A manual CI workflow publishes selected service images from committed tag files.
Current state
- Current
ExpenShare is live in production and continues to receive frequent updates.
Depth B / Technical inspectionInspect supporting evidence
Domains, provider boundaries, and evidence classifications.
Open
Inspect supporting evidence
Domain structure
- Wallet and accountsPersonal/shared accounts, pockets, balances, transfers, and account-affecting history.
- Transactions and activityIncome, expenses, transfers, filters, summaries, and reporting.
- Shared fairnessParticipant splits, projected group balances, and pair-level debt summaries.
- SettlementsProposals, confirmation, allocation, amendment, reversal, and optional wallet recording.
- GroupsShared membership, group-scoped financial activity, and feature-gated group funds.
- Loans and recurring activityLoan lifecycles, scheduled rules, generated activity, and reminders.
- Foreign exchangeCurrency catalogs, provider snapshots, user rates, and conversion workflows.
- Identity and notificationsEmail-verified accounts, social authentication, in-app notifications, and Web Push.
Integrations
- IdentityGoogle and Apple social authentication
Provide optional external account sign-in.
OAuth-based social authentication - Transactional EmailSMTP email delivery
Verify accounts and deliver transactional messages.
Environment-configured SMTP - Foreign ExchangeOpen Exchange Rates
Maintain exchange-rate snapshots for conversion workflows.
Provider API - NotificationsWeb Push
Deliver opted-in transaction and settlement notifications.
Standards-based browser push - Backup StorageS3-compatible object storage
Store encrypted database backup artifacts outside the database host.
S3-compatible object API
Capability evidence
- Product and system ownershipOwns product direction and the complete current engineering implementation.
- System architectureDesigned the Django API, service boundaries, and worker topology.
- API designExposes account, transaction, group, settlement, and supporting domain workflows.
- Domain modellingSeparates wallet history, shared fairness, settlements, and projected balances.
- Transactional workflowsPreserves audited financial inputs, allocations, reversals, and compensating adjustments.
- Authentication and authorizationEnforces access across personal accounts, shared accounts, groups, and participants.
- Background processingRuns recurring activity, reminders, push delivery, foreign exchange, and backups.
- Systems integrationIntegrates identity, email, foreign-exchange, push, and object-storage providers.
- Deployment and operationsMaintains isolated environments and independently releasable services.
- ObservabilityUses centralized structured logs and operational dashboards.
- Backup and recoveryAutomates PostgreSQL backup upload and retention safety.
- Performance optimizationMaintains read models and optimized query paths for financial summaries.