feat(segmentation-service): implement User Segmentation and Audience Service (Closes #363) - #387
Merged
Mkalbani merged 1 commit intoJun 26, 2026
Conversation
…Service (MindFlowInteractive#363) - New NestJS microservice at microservices/segmentation-service/ that builds and manages user cohorts for targeted campaigns, A/B testing, and personalization in the Quest Service platform. - Entities: Segment, Rule, Membership, SegmentEvent, AbExperiment, AbAssignment (TypeORM, PostgreSQL, jsonb for flexible metadata). - Rule engine with 14 operators (equals, notEquals, in, notIn, contains, notContains, gt, gte, lt, lte, between, exists, notExists, regex) and AND/OR combinators across rule order. Field resolution supports attributes.* dot-paths. - SegmentationService exposes segment CRUD, manual membership, rule add/ remove, evaluation sweeps, membership check, AND/OR overlap analysis, segment size metrics, and an A/B experiment endpoint with deterministic MD5-hashed variant assignment (sticky via Redis cache). - Real-time ingestSignal endpoint upserts a UserSignal in Redis and synchronously evaluates against the active segment set, with a source hierarchy (MANUAL > REALTIME > EVALUATION) so manual additions are never auto-removed by the engine. - A cron scheduler triggers periodic evaluation for active segments whose evaluationIntervalSeconds is reached. - Docker + docker-compose for Postgres + Redis + the service. Env template + README explaining every endpoint. - Tests: 17 unit tests across 3 suites (rule engine, segmentation service with relations-aware InMemoryRepo and stateful Redis cache mock, health controller). Type-check passes; lint clean (one non-blocking warning).
|
@Topmatrixmor2014 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
10 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements issue #363: User Segmentation and Audience Service Setup for the Quest Service platform.
A new, fully-independent NestJS microservice at
microservices/segmentation-service/creates and manages user cohorts for targeted campaigns, A/B testing, personalization, and analytics.Closes #363
Acceptance Criteria Mapping
Segment+Ruleentities with cascading on delete. Rules support 14 operators and AND/OR combinators.evaluateSegment, the cron scheduler, and the real-time ingest endpoint.POST /api/eventsupserts a UserSignal in Redis, then synchronously evaluates it against all active (or explicitly-named) segments, recording SegmentEvents.POST /api/segments/overlapintersectsmembershipuserIds across 2+ segments and reportsoverlappingUsers,totalUsers,overlapPercentage, andperSegmentSizes.GET /api/segments/:id/sizereturns cached + DB size withmeasuredAttimestamp. Cached in Redis namespacesegmentation:size:<id>and on theSegment.cachedSizecolumn. Dashboard endpoint summarises total membership.Dockerfile+docker-compose.yml(Postgres + Redis + service). No shared runtime withsrc/, makes its own TypeORM + ioredis connections.New Microservice
30+ files added under
microservices/segmentation-service/package.json,tsconfig.json,tsconfig.build.json,nest-cli.json,.env.example,.eslintrc.cjs,.prettierrc,.gitignoreDockerfile(multi-stage node:20-alpine build) +docker-compose.yml(postgres + redis + service)README.md(endpoint table, run instructions, rule schema, docker instructions)Entities (TypeORM / PostgreSQL)
Segment– cohort definition (slug, name, type, status, metadata, cachedSize, evaluationIntervalSeconds, lastEvaluatedAt)Rule– ordered predicate belonging to a Segment (field, operator, jsonb value, category, AND/OR combinator)Membership– user-in-segment link with a source enum (manual > realtime > evaluation)SegmentEvent– audit row for membership transitions (added / removed / refreshed)AbExperiment– A/B test bound to an optional SegmentAbAssignment– sticky user-to-variant mapping with a unique(experimentId, userId)index for idempotent insertsRule Engine (14 operators)
SegmentationRuleEngineService:equals,notEquals,in,notIn,contains,notContains,gt,gte,lt,lte,between,exists,notExists,regexattributes.*dot-paths, and bare attribute names.Segmentation Service
SegmentationService:create,update,delete,getSegment,listSegments)evaluateSegment(id, { userId | userIds | limit? })+ cron-drivenevaluateAllDueSegments()checkMembership,addManualMembers,removeManualMember,listMembers, plus auto-management during evaluationoverlap({ segmentIds })createExperiment,assignVariant(id, dto)with deterministic MD5-hashed buckets + sticky Redis cachegetSize(id)(cache → DB fallback) and a service-wide dashboardSEGMENTATION_SEED_DEFAULTS=falseReal-Time Ingest
POST /api/events:segmentsToReEvaluateset or every active segment, runs the rule engine, and books-adds/removes memberships. Explicitly-requested segments never auto-remove members.Redis Cache (
RedisCacheService)Namespace
segmentation:*:segmentation:user:{userId}– user signal JSONsegmentation:size:{segmentId}– cached size countersegmentation:assignment:{experimentKey}:{userId}– sticky variantScheduler (
SegmentationScheduler)Cron task that runs the due-segment evaluation queue every minute (gated by
SEGMENTATION_SCHEDULER_ENABLED).API Surface
/api/health/api/segments/api/segments/dashboard/api/segments/overlap/api/segments/api/segments/:id/api/segments/:id/api/segments/:id/api/segments/:id/rules/api/segments/:id/rules/:ruleId/api/segments/:id/evaluate/api/segments/:id/membership/api/segments/:id/membership/:userId/api/segments/:id/members/api/segments/:id/size/api/segments/:id/check/api/events/api/experiments/api/experiments/:id/assignHow to Run
By default the service listens on
PORT=3023and exposes Swagger friendly JSON at/api.Testing
npm run type-check– 0 errors (tsc --noEmit)npm run lint:check– 0 errors, 1 non-blockinganywarning in the unit-test mocknpm run test– 17 / 17 passing across:segmentation-rule-engine.service.spec.ts(covers every operator + combinator + dot-notation + inactive rules)segmentation.service.spec.ts(segment CRUD, duplicate-slug rejection, overlap with shared / disjoint / empty segments, deterministic A/B variants, sticky-cache replay, size metric)app.controller.spec.ts(health endpoint)The spec mocks use a relations-aware
InMemoryRepoand a stateful Redis cache mock so the service can be exercised without a database.Diff Stats
Checklist
npm install && npm run type-checknpm run testfor the entire test suite (17 / 17)NODE_ENV !== production(auto-synchronize schema for dev)docker compose up --buildMindFlowInteractive/quest-service:mainfrom the forkTopmatrixmor2014/quest-service:feat/segmentation-service-363upstream/mainso the diff is conflict-freemicroservices/segmentation-service/Notes for Reviewers
upstream/mainis clean; this PR only adds the new service directory.SEGMENTATION_SEED_DEFAULTS.experiment.keyin the hash.