Skip to main content
This guide covers the development workflow, architecture patterns, and best practices for contributing to Divvy.

Project Structure

Divvy follows a clean architecture pattern organized by feature and layer:

Key Directories

1

backend/ - Data Layer

Contains repository interfaces and Supabase implementations:
  • GroupRepository.kt / SupabaseGroupRepository.kt
  • ExpensesRepository.kt / SupabaseExpensesRepository.kt
  • AuthRepository.kt / SupabaseAuthRepository.kt
  • ProfilesRepository.kt, MemberRepository.kt, BalanceRepository.kt
  • DataResult.kt - Sealed class for async operation states
Example repository pattern:
2

ui/ - Presentation Layer

Organized by feature, each with Views/ and ViewModels/ subdirectories:
  • home/ - Activity feed and quick stats
  • groups/ - Group list and creation
  • groupdetail/ - Group expenses and members
  • expenses/ - Expense list
  • splitexpense/ - Create and split expenses
  • assignitems/ - Itemized receipt splitting
  • ledger/ - Settlement tracking
  • profile/ - User profile settings
  • navigation/ - NavHost and destination definitions
3

models/ - Domain Models

Data classes representing core business entities:
  • Group.kt - Group information and balance
  • Expense.kt - Individual expense records
  • UserProfile.kt - User account data
  • Split.kt - Expense split allocation
  • LedgerEntry.kt - Settlement records
4

components/ - Shared UI

Reusable Compose components:
  • PrimaryButton.kt - Primary CTA button
  • OutlineButton.kt - Secondary button
  • GroupIcon.kt - Group avatar component
  • AuthTextField.kt - Styled input field
5

di/ - Dependency Injection

Hilt modules for dependency management:
  • AppModule.kt - Repository bindings
  • NetworkModule.kt - Supabase client provider

MVVM Architecture

Divvy uses the Model-View-ViewModel (MVVM) pattern with Jetpack Compose:

Workflow

  1. View - Composable functions observe ViewModel state
  2. ViewModel - Manages UI state and business logic, calls repositories
  3. Repository - Fetches data from Supabase, returns Flow<DataResult<T>>

Example: HomeViewModel Pattern

See app/src/main/java/com/example/divvy/ui/home/ViewModels/HomeViewModel.kt:1 for the full implementation.

Adding a New Feature

Follow these steps to add a new feature to Divvy:
1

Define the domain model

Create data classes in models/ if needed:
2

Create repository interface

Define data access in backend/:
3

Register dependency in AppModule

Bind the implementation in di/AppModule.kt:1:
4

Create ViewModel

Add state management in ui/transactions/ViewModels/:
5

Build the UI

Create Compose screens in ui/transactions/Views/:
6

Add navigation route

Define the destination in ui/navigation/AppDestination.kt:1:
Add to NavHost in ui/navigation/AppNavHost.kt:1:

Coding Standards

Kotlin Style

  • Follow Kotlin coding conventions
  • Use meaningful variable names: groupId not gid
  • Prefer val over var for immutability
  • Use data classes for models
  • Leverage Kotlin coroutines for async operations

Compose Best Practices

  • State hoisting: Pass state and events down to composables
  • Stateless composables: Keep UI functions pure when possible
  • Preview annotations: Add @Preview for UI components
  • Modifier ordering: Size → Padding → Visual effects
  • Remember state: Use remember for UI-only state
Example:

Dependency Injection with Hilt

Divvy uses Hilt for dependency injection:
  • Mark Application class with @HiltAndroidApp (see DivvyApplication.kt:1)
  • Annotate ViewModels with @HiltViewModel
  • Use @Inject constructor() for repository dependencies
  • Register bindings in @Module classes under di/

Working with Supabase

Database Schema

Divvy’s Supabase database includes these main tables:
  • profiles - User account information
  • groups - Group metadata
  • group_members - Group membership junction table
  • expenses - Expense records
  • expense_splits - How expenses are divided
  • ledger_entries - Settlement transactions

Row Level Security (RLS)

Supabase RLS policies ensure users can only access their own data:
  • Users can only read groups they’re members of
  • Only group members can create expenses for that group
  • Profiles are readable by authenticated users
When adding new tables or modifying queries, ensure RLS policies are properly configured in the Supabase dashboard to prevent data leaks.

Making Queries

Use the Supabase Kotlin SDK via repositories:
See app/src/main/java/com/example/divvy/backend/SupabaseGroupRepository.kt:1 for real examples.

Feature Flags

Divvy uses feature flags to enable/disable features during development. Configuration: app/src/main/java/com/example/divvy/FeatureFlags.kt:1
Flags are set in local.properties and injected at build time:
Usage:
Add new feature flags to FeatureFlags.kt and app/build.gradle.kts as needed for experimental features.

Team Workflow

Issue Tracking with Linear

The team uses Linear for project management:
  • Pick an issue: Check the current sprint and assign yourself to a task
  • Update status: Move issues through “Todo”, “In Progress”, “In Review”, “Done”
  • Link PRs: Reference Linear issue IDs in commit messages (e.g., DIV-123: Add transaction list)

UI Design with Figma

  • Check designs first: Before implementing UI, review mockups in Figma
  • Match styles: Use the design system defined in ui/theme/ to match Figma specs
  • Ask questions: If designs are unclear, ping the team on Discord

Communication on Discord

  • Daily updates: Share progress and blockers
  • Code reviews: Request reviews in the dev channel
  • Quick questions: Use Discord for synchronous debugging help

Git Workflow

  1. Create a feature branch: git checkout -b feature/transaction-list
  2. Make commits with clear messages: git commit -m "Add transaction repository"
  3. Push and open a PR: git push origin feature/transaction-list
  4. Request review from team members
  5. Merge after approval
Always pull the latest changes from main before starting new work to avoid merge conflicts.

Next Steps