Skip to content

Objectuve Architecture

The Objectuve repository is structured as a monorepo, consisting of three main applications (rails_api, ionic_frontend, admin_dashboard) and a marketing site (marketing_landing).

System Design Diagram

graph TB
    subgraph Clients["Client Layer"]
        WEB["🌐 Web Browser<br/><i>app.objectuve.com</i>"]
        IOS["📱 iOS App<br/><i>Capacitor</i>"]
        ANDROID["📱 Android App<br/><i>Capacitor</i>"]
        ADMINUI["🌐 Admin Dashboard<br/><i>admin.objectuve.com</i>"]
    end

    subgraph Frontend["ionic_frontend — Vue 3 / Ionic 8 / Vite"]
        direction TB
        ROUTER["Vue Router<br/><i>@ionic/vue-router</i>"]

        subgraph Views["Views"]
            DASH["Dashboard"]
            GOALS["Goals / Goal Detail"]
            FEED["Activity Feed"]
            COMMUNITIES["Communities"]
            ACHIEVEMENTS["Achievements"]
            SETTINGS["Settings"]
        end

        subgraph Composables["Composables"]
            USE_AI["useAiInsights<br/>useAiCoach"]
            USE_GOAL["useGoalForm<br/>useProgressData"]
            USE_FEED["useUnifiedFeed"]
            USE_NOTIF["useNotifications"]
            USE_CLERK["useClerkSync"]
        end

        subgraph Components["Component Library (56 stories)"]
            HABIT_UI["TodaysHabits<br/>HabitCalendar<br/>HabitCompletionRing"]
            CHART_UI["ProgressChart<br/>PaceIndicator<br/>StreakVisualization"]
            FEED_UI["FeedItemCard<br/>CheckInPromptCard"]
            CORE_UI["GoalGridCard<br/>DashboardHero<br/>ActionHub"]
        end

        PINIA["Pinia Stores"]
        GQL_CONST["graphql/*<br/><i>Context-split queries & mutations</i>"]
        APOLLO["Apollo Client<br/><i>BatchHttpLink + ActionCableLink</i>"]
    end

    subgraph Auth["Authentication"]
        CLERK["Clerk<br/><i>RS256 JWT via JWKS</i>"]
    end

    subgraph Backend["rails_api — Rails 8.1.2 / Ruby 4.0"]
        direction TB
        GQL_CTRL["GraphqlController<br/><i>POST /graphql</i>"]
        CABLE["ActionCable<br/><i>WS /cable</i>"]

        subgraph GQL_Layer["GraphQL Layer"]
            QUERY_TYPE["QueryType<br/><i>20+ queries</i>"]
            MUT_TYPE["MutationType<br/><i>25+ mutations</i>"]
            GQL_TYPES["Types<br/><i>GoalType, UserType,<br/>UnifiedFeedType, etc.</i>"]
        end

        subgraph Interactions["Interactions (Business Logic)"]
            GOAL_INT["AddGoal · UpdateGoal<br/>CheckInHabit · UseStreakFreeze"]
            PROGRESS_INT["CalculateGoalProgress<br/>BuildUnifiedFeed"]
            USER_INT["SignUp · UpdateUser<br/>AddMoodLog"]
            NOTIF_INT["SendPushNotification"]
        end

        subgraph Models["Models"]
            USER_M["User<br/><i>PublicRecord</i>"]
            GOAL_M["Goal<br/><i>PublicRecord</i>"]
            HABIT_M["HabitCompletion"]
            EVENT_M["GoalEvent<br/><i>PublicRecord</i>"]
            COMM_M["Community<br/><i>PublicRecord</i>"]
            MOOD_M["MoodLog<br/><i>PublicRecord</i>"]
        end

        subgraph Services["Services"]
            AI_COACH["Ai::CoachService<br/><i>Langchain gem</i>"]
            GAMIFICATION["GamificationService"]
        end

        subgraph Jobs["Background Jobs (Crono)"]
            JOB_STREAK["ProcessHabitStreaksJob<br/><i>daily 08:00</i>"]
            JOB_REMIND["GenerateReminderNotificationsJob<br/><i>daily 09:30</i>"]
            JOB_AI["GenerateAiCheckInPromptsJob<br/><i>daily 10:00</i>"]
            JOB_ACTIVITY["GenerateActivityReminderJob<br/><i>daily 12:00</i>"]
        end

        JWT_VERIFY["ClerkJwtVerifier<br/><i>RS256 JWKS validation</i>"]
    end

    subgraph Infrastructure["GCP Infrastructure (us-central1)"]
        direction TB
        CLOUD_RUN["Cloud Run<br/><i>enkidu-api-production</i>"]
        CLOUD_SQL["Cloud SQL<br/><i>PostgreSQL 15</i>"]
        FIREBASE["Firebase Hosting<br/><i>enkidu-app</i>"]
        ARTIFACT["Artifact Registry<br/><i>enkidu-registry</i>"]
        REDIS["Redis<br/><i>Sidekiq + ActionCable</i>"]
    end

    subgraph External["External Services"]
        LLM["LLM Provider<br/><i>OpenAI / Anthropic /<br/>Gemini / Ollama</i>"]
        PUSH["FCM / APNs<br/><i>Push Notifications</i>"]
        GCS["Google Cloud Storage<br/><i>File Storage</i>"]
        SENTRY["Sentry<br/><i>Error Tracking</i>"]
        MAILTRAP["Mailtrap<br/><i>Email</i>"]
    end

    %% Client connections
    WEB --> FIREBASE
    IOS --> CLOUD_RUN
    ANDROID --> CLOUD_RUN
    FIREBASE --> APOLLO

    %% Frontend internal flow
    ROUTER --> Views
    Views --> Composables
    Views --> Components
    Composables --> GQL_CONST
    GQL_CONST --> APOLLO
    PINIA --> Views

    %% Auth flow
    CLERK --> USE_CLERK
    CLERK --> JWT_VERIFY
    APOLLO -- "SessionToken header" --> GQL_CTRL

    %% Backend internal flow
    GQL_CTRL --> GQL_Layer
    CABLE --> GQL_Layer
    GQL_Layer --> Interactions
    Interactions --> Models
    Interactions --> Services
    Models --> CLOUD_SQL
    Services --> LLM
    Jobs --> Interactions
    Jobs --> NOTIF_INT
    NOTIF_INT --> PUSH

    %% Infrastructure connections
    CLOUD_RUN --> CLOUD_SQL
    CLOUD_RUN --> REDIS
    REDIS --> CABLE
    REDIS --> Jobs

    %% External connections
    Backend --> GCS
    Backend --> SENTRY
    Backend --> MAILTRAP

    %% Styling
    classDef clientStyle fill:#e3f2fd,stroke:#1565c0,color:#0d47a1
    classDef frontendStyle fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20
    classDef backendStyle fill:#fff3e0,stroke:#e65100,color:#bf360c
    classDef infraStyle fill:#f3e5f5,stroke:#6a1b9a,color:#4a148c
    classDef externalStyle fill:#fce4ec,stroke:#c62828,color:#b71c1c
    classDef authStyle fill:#fffde7,stroke:#f9a825,color:#f57f17

    class WEB,IOS,ANDROID clientStyle
    class ROUTER,DASH,GOALS,FEED,COMMUNITIES,ACHIEVEMENTS,SETTINGS,USE_AI,USE_GOAL,USE_FEED,USE_NOTIF,USE_CLERK,HABIT_UI,CHART_UI,FEED_UI,CORE_UI,PINIA,GQL_CONST,APOLLO frontendStyle
    class GQL_CTRL,CABLE,QUERY_TYPE,MUT_TYPE,GQL_TYPES,GOAL_INT,PROGRESS_INT,USER_INT,NOTIF_INT,USER_M,GOAL_M,HABIT_M,EVENT_M,COMM_M,MOOD_M,AI_COACH,GAMIFICATION,JOB_STREAK,JOB_REMIND,JOB_AI,JOB_ACTIVITY,JWT_VERIFY backendStyle
    class CLOUD_RUN,CLOUD_SQL,FIREBASE,ARTIFACT,REDIS infraStyle
    class LLM,PUSH,GCS,SENTRY,MAILTRAP externalStyle
    class CLERK authStyle

Reading the Diagram

ColorLayerDescription
🔵 BlueClientBrowsers and native mobile apps connecting to the platform
🟢 GreenFrontendVue 3 / Ionic SPA — views, composables, components, Apollo Client
🟡 YellowAuthClerk handles all authentication — issues RS256 JWTs verified server-side
🟠 OrangeBackendRails API — GraphQL endpoint, interactions, models, background jobs
🟣 PurpleInfrastructureGCP Cloud Run, Cloud SQL, Firebase Hosting, Redis
🔴 RedExternalThird-party services (LLM providers, push notifications, storage, email)

Request Flow

User action → Vue Router → View → Composable → graphql.js → Apollo Client
  → POST /graphql (SessionToken header)
  → GraphqlController → QueryType/MutationType → Interaction → Model → PostgreSQL
  → Response → Apollo Cache → Reactive UI update

Real-time Flow

Server event (notification, badge, level-up)
  → UserNotification.generate → ActionCable subscription trigger
  → WS /cable → Apollo subscription → useNotifications composable → Toast/Card UI

Background Job Flow

Crono scheduler (daily at configured times)
  → Job.perform → Interaction (business logic) → Model (database)
  → SendPushNotification → FCM/APNs → Device push notification
  → UserNotification.generate → ActionCable → Live in-app notification
text
├── ionic_frontend/        // Main Mobile/Web Application (Ionic, Vue 3, Vite)
├── admin_dashboard/       // Dedicated Admin Portal (Vue 3, Vite, Tailwind, HeadlessUI)
├── rails_api/             // Backend API (Rails 8, GraphQL)
├── marketing_landing/     // Static Marketing Website
├── docker-compose.yml     // Docker orchestration
├── .semaphore/            // CI/CD Configuration
└── ...                    // Other configuration files

1. ionic_frontend — Main Mobile/Web Application

  • Framework: Ionic 8 (Vue 3)
  • Build Tool: Vite
  • Purpose: The primary user-facing application for mobile (iOS/Android) and web. Handles user interaction, data presentation, and state management.

Key Directories

  • src/views/: Application pages (e.g., Dashboard.vue, Goals.vue, ActivityFeed.vue, Settings.vue).
  • src/components/: Reusable UI components ensuring consistent design.
  • src/composables/: Vue composables for shared logic (e.g., useAiInsights.ts, useAiCoach.ts, useProgressData.ts, useUnifiedFeed.ts, useGoalForm.ts).
  • src/stores/: Pinia stores for local state management.
  • src/constants/graphql/: All GraphQL query and mutation definitions (18 modules + barrel index.js).
  • src/router/: Vue Router configuration.
  • src/theme/: Global styling and theme variables.
  • src/apollo-client.ts: Apollo Client configuration inc. Auth & Subscriptions.
  • .storybook/: Storybook configuration, decorators, and mocks.

Key Functionalities

  • User Interface: Comprehensive UI for Goals, Communities, Dashboard, and Settings.
  • Authentication: JWT-based auth via Apollo Middleware (SessionToken header).
  • Real-time Updates: Integration with ActionCable for live data (e.g., goal updates).
  • Mobile Navigation: BottomTabBar component handles tab navigation on mobile (≤767px); sidebar handles desktop.
  • Coach Insights: useAiInsights composable provides context-aware insight cards on every major page with 15-minute caching and 24-hour dismissal persistence.
  • Mood Tracking: MoodCheckIn modal lets users log emotional state (6 moods) optionally linked to a goal.
  • Habit Tracking: Recurring habit goals with one-tap check-in via TodaysHabits dashboard widget. HabitCalendar heatmap and HabitCompletionRing progress indicator on goal detail.
  • Progress Visualization: ProgressChart bar chart on goal detail with day/week/month grouping, PaceIndicator (ahead/on track/behind), and StreakVisualization for habit goals.
  • Unified Activity Feed: /feed view (ActivityFeed.vue) merging ally check-ins, community posts, and own goal events with filter tabs and pagination via FeedItemCard component.
  • Check-In Prompts: CheckInPromptCard on dashboard showing personalized Coach-generated nudges referencing specific goals, streaks, and milestones.
  • Offline Capability: (Potential via Capacitor/PWA standards).
  • Native Integration: Capacitor for accessing native device features (Camera, Haptics).

2. rails_api — Backend API

  • Framework: Ruby on Rails 8.1.2 (API Only)
  • Language: Ruby 4.0
  • Purpose: The central source of truth for data, business logic, and authentication.

Key Directories

  • app/graphql/: GraphQL schema definitions (types, mutations, resolvers).
  • app/interactions/: Business logic encapsulated in interaction objects (e.g., AddGoal, SignUp, AddMoodLog, CheckInHabit, CalculateGoalProgress, BuildUnifiedFeed).
  • app/models/: ActiveRecord models for database interaction.
  • app/channels/: ActionCable channels for websocket connections.
  • app/jobs/: Sidekiq jobs for background processing.

Key Functionalities

  • GraphQL API: Exposes a rich API for the frontend to consume.
  • Authentication: Handles User Sign Up/In and JWT generation.
  • Business Logic: "Interaction" pattern ensures clean separation of concerns.
  • Background Jobs: Uses Sidekiq/Crono for scheduled tasks (habit streak processing, AI check-in prompts, reminder notifications, past due alerts).

3. admin_dashboard — Admin UI

  • Framework: Vue 3 (Vite)
  • Library: HeadlessUI, TailwindCSS
  • Purpose: A standalone administrative frontend exposing operations securely disjointed from the public app bundle. Provides audit tracking, moderation queues, and user role overrides.

4. marketing_landing — Marketing Site

  • Structure: Static HTML/CSS/JS site.
  • Purpose: Public-facing landing page for the product.
  • Key Files: index.html, blog.html, css/, js/.

4. Database Layer (PostgreSQL)

  • Purpose: Relational database for persistent storage.
  • Schema: Managed via Rails Active Record Migrations (rails_api/db/migrate).
  • Key Models: User, Goal, Community, GoalEvent, MoodLog, HabitCompletion.

5. Docker Configuration

  • Purpose: Containerization for development and potentially production.
  • Key Files:
    • docker-compose.yml: Orchestrates db (Postgres), api (Rails), and frontend.
    • rails_api/Dockerfile.ruby: Dockerfile for the Rails API.

Observability

The application uses a GCP-native + Sentry observability stack:

  • Structured Logging: Lograge emits JSON request logs (method, path, status, duration, user_id, graphql_operation) auto-parsed by GCP Cloud Logging
  • Error Tracking: Sentry on both backend (Rails) and frontend (Vue) with user context and performance tracing
  • Health Check: GET /health probes database, Redis, and Sidekiq — returns component-level status
  • Performance Metrics: Sentry APM with GraphQL::Tracing::SentryTrace for query-level traces and frontend web vitals
  • Alerting: GCP Cloud Monitoring (uptime, latency, error rate, DB metrics) + Sentry (error spikes, new issues)
  • Dashboards: GCP Cloud Monitoring (infrastructure) + Sentry (application errors and performance)

For full details, see Observability, Alerting, and Dashboards.

API Endpoints

The application primarily exposes a single GraphQL endpoint, but technically serves:

  • POST /graphql: The main entry point for all queries and mutations.
  • WS /cable: WebSocket endpoint for ActionCable subscriptions.
  • GET /health: Detailed health check (database, Redis, Sidekiq status).
  • GET /up: Simple health check for load balancers.

Key GraphQL Queries

  • user(id: ID) / users: Fetch specific user or list of users.
  • goal(id: ID) / goals: Fetch goal details or list of goals for a user.
  • publicGoal(id: ID): Fetch limited public details of a goal.
  • community(id: ID) / communities: Fetch community details or browse available communities.
  • goalEvent(id: ID): Fetch details about a specific update/event on a goal.
  • goalKinds / goalCategories: Fetch metadata about available goal types and categories.
  • communitySuggestions: Get recommended communities for the current user.
  • isFollowingGoal(id: ID): Check if the current user follows a specific goal.
  • goalProgressData(id: ID, period?, lookbackDays?): Progress visualization data for a goal (event frequency, pace, streak data).
  • unifiedFeed(limit?, offset?): Paginated unified activity feed merging ally activity, community posts, and own goal events.

Key GraphQL Mutations

  • signIn / oAuthSignIn: Authenticate user and return session token.
  • signUp: Register a new user.
  • addGoal / updateGoal: Create or modify a goal.
  • addGoalEvent / updateGoalEvent: Post an update to a goal.
  • joinCommunity / addGoalToCommunity: Interaction with communities.
  • updateUser / updateUserPhoto: Modify user profile information.
  • toggleFollowGoal: Follow/unfollow a specific goal.
  • toggleGoalEventEncouragement: "Like" or encourage a goal update.
  • addGoalEventComment: Add a comment to a goal update.
  • addMoodLog(mood, note?, goalId?): Record a mood check-in for the current user, optionally linked to a goal.
  • checkInHabit(goalId): One-tap habit check-in for the current day.
  • useStreakFreeze(goalId): Use a streak freeze to preserve a habit streak.

Key Data Flows

Goal Creation

  1. Frontend: User submits goal form in ionic_frontend.
  2. GraphQL: Mutation addGoal is sent to /graphql.
  3. Rails: GraphqlController receives request.
  4. Resolver: Delegates to AddGoal interaction.
  5. Interaction: Validates input, creates Goal record, potentially triggers background jobs.
  6. Response: JSON payload returned to frontend; cache updated.

Authentication

  1. Frontend: User signs in via Clerk (email, Google OAuth, or other configured providers).
  2. Clerk: Issues an RS256 JWT after successful authentication.
  3. Frontend: Retrieves the JWT via Clerk.session.getToken() and attaches it as the SessionToken header (PascalCase) on every GraphQL request.
  4. Rails: ClerkJwtVerifier decodes the token via JWKS. ClerkUserSync finds or creates the local User record from the Clerk profile.

Mood Check-In

  1. Frontend: User opens MoodCheckIn modal, selects mood and optional goal/note.
  2. GraphQL: addMoodLog mutation sent with mood, optional goalId, optional note.
  3. Rails: AddMoodLog interaction creates a MoodLog record linked to the current user.
  4. Response: MoodLog payload returned; modal closes.

Environment Variables

Rails API

  • DATABASE_HOST, DATABASE_USERNAME, DATABASE_PASSWORD: Database connection.
  • REDIS_URL: Redis connection for Sidekiq/ActionCable.
  • GCP_PROJECT_ID: Google Cloud project ID.
  • GCS_BUCKET_NAME: Google Cloud Storage bucket for media uploads — per-environment, pinned in each deploy/*.yaml manifest (enkidu-storage production, enkidu-storage-staging staging; see Deployment § Staging wrote live uploads into production's GCS bucket). Uploaded objects carry a Cache-Control: public, max-age=300 header — see Deployment § Public media Cache-Control for how long a deleted photo stays fetchable and why.
  • MAILTRAP_API_TOKEN: Transactional email delivery.
  • SENTRY_DSN: Error tracking.

Ionic Frontend

  • VITE_API_URL: URL of the backend API (e.g., localhost:3000 or production URL).
  • Capacitor configurations for Android/iOS builds.

Features

Organization via Interactions

The backend allows for strict organization by encapsulating every "action" a user can take into an Interaction class (in app/interactions). This keeps controllers and GraphQL resolvers thin and creates a clear directory of all available business capabilities.

Mobile-First Design

The ionic_frontend is built to be deployed as a native app using Capacitor, utilizing native plugins while sharing a single codebase for the web. The BottomTabBar component provides native-feeling tab navigation on mobile and is hidden on desktop where the sidebar takes over.

Real-Time Interactions

ActionCable flows real-time updates to the client, allowing features like "New Goal Feed" or "Live Comments" to update without page refreshes.

Coach Insights System

The useAiInsights composable provides page-scoped contextual insights. Each page (dashboard, goals, goal-detail, achievements, communities, admin) generates an insight based on the user's current data. Insights are cached for 15 minutes at the module level and dismissals persist for 24 hours via localStorage.

Last updated: 2026-08-30 — component-library diagram node renamed MomentumBarDashboardHero, which superseded it (OBJ-3016).

Loading…