Skip to content

Bounded Contexts

Each folder under apps/api/src/modules/<name>/ is a bounded context. Cross-context service imports are forbidden by ESLint rule no-cross-context-import. Integration happens through the NestJS EventEmitter2 event bus.

Context Map

mermaid
graph LR
    Identity -->|invite_code.used| Reputation
    Identity -->|invite_code.used| InviteTree
    Interests -->|user_selected| Reputation
    Posts -->|post.created| Reputation
    Posts -->|post.replied| Reputation
    Posts -->|post.reacted| Reputation
    Posts -->|post.bookmarked| Reputation
    Posts -->|post.reposted| Reputation
    Posts -->|emoji_pay.sent| Economy
    Posts -->|post.created| Contribution
    Economy -->|wallet.bootstrap| Identity
    Reputation -->|reputation.changed| Notifications
    Social -->|circle.invited| Notifications
    Messaging -->|message.posted| Notifications

Contexts Reference

identity

Aggregate roots: User, InviteCode, Session, Persona

Responsibilities:

  • Invite-only registration: reserve → register flow with 30-minute TTL
  • Invite code generation: format REG-XXXXXX, allocation formula clamp(5 + rep/50 + age/14d, 5, 30), default batch 5
  • JWT-based auth: login, refresh, logout
  • Personas: max 5 per user, one default (Main), accent color tokens
  • Account lifecycle: password reset, GDPR deletion, ban/unban
  • Profile: username, bio, avatar, city, country

Events emitted:

  • identity.invite_code.used — when registration consumes a code
  • identity.user_registered — new user created

Events consumed: none (bootstrap EmojiWallet is triggered by economy module on identity.user_registered)


interests

Aggregate roots: Interest (catalog), UserInterest

Responsibilities:

  • 3-level catalog (L1 category → L2 sub-interest → L3 deep-interest)
  • Catalog tree cached for 10 minutes in-process
  • Per-user depth 1–5 (Curious / Reading / Practicing / Deep / Lifework)
  • Self-rated expertise 1–5 (distinct from depth)
  • Rollup reputation to parents using RATIOS = [1.0, 0.5, 0.25, 0.12, 0.06]

Events emitted:

  • interests.user_selected
  • interests.depth_changed

Events consumed: none


reputation

Aggregate roots: ReputationEvent (ledger), ReputationSnapshot, ReputationVote, ReputationAppeal

Responsibilities:

  • Per-interest reputation only — no global aggregate exposed publicly
  • Append-only ledger (reputation_events); snapshots materialised in reputation_snapshots
  • Peer voting: +1 / 0 / -1 per (voter, target, interest) triple
  • Sybil dampening: votes from users with very low total rep are down-weighted
  • Level labels: Curious (0–9) / Reader (10–29) / Practitioner (30–49) / Adept (50–69) / Master (70–89) / Lifework (90+)
  • Appeal + redemption flow: pending → accepted/rejected, optional restoreDelta

Events consumed:

  • interests.user_selected → +5 per selected interest (one-shot bootstrap)
  • identity.invite_code.used → +3 to inviter in their top interest
  • posts.created → +3 to author in post's interest
  • posts.replied → +2 to replier
  • posts.reacted (first unique per user) → +1 to post author
  • posts.bookmarked (first unique) → +1 to post author
  • posts.reposted (first unique) → +1 to post author

social

Aggregate roots: Circle, CircleMember, CircleInvitation, UserFollow, UserBlock, UserMute

Responsibilities:

  • Circle CRUD (max 10 per user)
  • Built-in circle kinds: general | family | work | inner
  • Bidirectional consent: owner invites → invitee accepts/declines
  • Bond levels: inner-circle | acquaintance
  • Unidirectional follow graph
  • Block / mute between users

Events emitted:

  • circle.invited
  • circle.member_joined

posts

Aggregate roots: Post, PostReply, PostReaction, PostBookmark, PostInterest, PostCoAuthor, PostSeries, PostBoost

Responsibilities:

  • All content formats: note | essay | photo | video | quote | thread | repost
  • Visibility: public | circles | inner
  • Content depth 0–5 (surface → thesis)
  • Multi-interest tagging (up to 5 via PostInterest)
  • Co-authorship: invite → accept/decline, splitShare 0–100 %, primary author keeps ≥ 10 %
  • Threaded replies with kind: reply | reaction | criticism | contribution
  • Scheduled publish (publishAt), soft delete, edit, pin (max 3)
  • #journalism tag: gate rep ≥ 20 in any interest
  • Series: create, order, follow for notifications
  • Materialised counters: reactions, replies, shares, bookmarks, views

Events emitted:

  • posts.created
  • posts.replied
  • posts.reacted
  • posts.bookmarked
  • posts.reposted

ai-assistant

Aggregate roots: AssistantSession, AssistantTurn, AssistantSuggestion

Responsibilities:

  • 5-question reflection flow; each session = 5 turns
  • Anthropic Claude Sonnet 4.6 via Vercel AI SDK for question generation and interest mapping
  • Scripted fallback when ANTHROPIC_API_KEY is absent
  • POST /ai/map-interests — LLM maps free-text to catalog interests
  • POST /ai/apply-suggestions — writes accepted suggestions to UserInterest
  • POST /ai/enhance-post — AI content enhancement in composer
  • AssistantSuggestion.confidence: 0–1, depth: 1–5, reason: max 280 chars

economy

Aggregate roots: EmojiWallet, EmojiPayment (append-only), PostBoost

Responsibilities:

  • EmojiPay: send mana on a post or reply with an emotion
  • 8 emotions: love | awe | joy | curiosity | gratitude | insight | calm | courage
  • 6 tiers: 0=1, 1=3, 2=10, 3=25, 4=50, 5=100 mana
  • Rep thresholds per tier (in post's primary interest): 0/0/10/20/35/50
  • Wallet: 50 starter mana, 5/day regen, 50 regen cap
  • Co-author mana split on accepted PostCoAuthor rows
  • Boost: tier 1 = ×1.5 for 24 h (10 mana), tier 2 = ×2 for 48 h (25 mana)
  • Nightly regen cron; boost expiry cron
  • All financial rows are append-only. Reversal = new row with reversed: true.

notifications

Aggregate roots: Notification, NotificationPreference, PushToken

Responsibilities:

  • In-app notification inbox
  • Expo Push Notifications (ExponentPushToken[...])
  • Per-kind toggles stored as JSON in NotificationPreference.kinds
  • Per-channel toggles: push | email | inApp
  • Notifications created by subscribing to events from other contexts

messaging

Aggregate roots: Conversation, ConversationMember, Message, MessageReaction

Responsibilities:

  • DM and group conversations (up to 50 members)
  • DM restricted to users who share at least one circle
  • Per-conversation mute (ConversationMember.mutedAt)
  • Message emoji reactions (grapheme validated server-side)
  • Real-time delivery via Centrifugo

wiki

Aggregate roots: WikiEntry, WikiProposal, WikiVote

Responsibilities:

  • Per-interest knowledge base (one canonical entry per interest)
  • Propose → community vote → auto-approve at netVotes ≥ 3
  • Rep gate for proposing/voting: WIKI_MIN_REP = 5 in interest
  • WikiVote.value = +1 | -1; WikiProposal.netVotes is materialised counter
  • Status: pending | approved | rejected

contribution

Aggregate roots: ContributionEvent (append-only), ContributionSnapshot

Responsibilities:

  • Tracks overall platform contribution per user
  • Event kinds: post_created | reply_created | reaction_given | bookmark_given | emoji_pay_sent
  • Invite-tree inheritance: 50 % to inviter / 25 % to grand-inviter / 10 % to great-grand-inviter
  • Snapshot recomputed nightly; also updated incrementally

discovery

Aggregate roots: ConnectionIntent

Responsibilities:

  • Intent-based discovery: networking | friends | romantic | mentor | mentee | collab
  • GET /discovery/candidates — users with matching intent
  • GET /discovery/mentors — users with expertise gap ≥ N in a shared interest
  • isFollowing wired to follow graph
  • isActive: false opts user out of discovery entirely

invite-tree

Aggregate roots: InviteNode

Responsibilities:

  • Materialised path tree (dot-separated UUIDs) of "who invited whom"
  • Subtree queries via path LIKE 'root_uuid.%'
  • Ancestors / descendants / lineage endpoints
  • Used by Contribution context for inheritance ratios

admin (cross-cutting)

No aggregate roots. Admin endpoints proxy to other contexts.

Responsibilities:

  • Users: list, role change, ban/unban, GDPR delete
  • Posts: list, hide/unhide
  • Reports: review queue (open / reviewed / dismissed)
  • Reputation: votes viewer, appeal resolution
  • Economy: wallet overview, payment history
  • Wiki: proposal management
  • Feature flags: toggle emoji-pay | wiki | personas-ui | journalism
  • Audit log: append-only log of all privileged mutations
  • Broadcast notifications

shared (not a context)

Shared infrastructure used by all contexts:

PathPurpose
shared/auth/JwtAuthGuard, JwtStrategy, AdminGuard
shared/access/AccessMatrixService, @RequiresRep decorator
shared/prisma/PrismaService (singleton)
shared/events/Typed event definitions (@regulus/domain)
shared/guards/ThrottlerGuard configuration
shared/feature-flags/FeatureFlagService + @RequiresFlag guard
shared/realtime/CentrifugoService
shared/email/EmailService (Resend)
shared/health/GET /health liveness probe

Regulus — invite-only social-knowledge platform