AI-powered Customer Lifecycle & Value Management Platform
CustomerPulse is a realistic Customer Lifecycle Management (CLM) and Customer Value Management (CVM) platform built as a practical backend engineering project.
The project is designed to explore how a modern customer intelligence platform can evolve from a well-structured modular monolith into an event-driven, scalable and AI-powered architecture.
The primary focus is not simply building CRUD APIs, but designing the foundations required for:
- Customer lifecycle management
- Customer value calculation
- Customer scoring
- Next Best Action (NBA)
- Customer 360
- Asynchronous processing
- Event-driven architecture
- AI/ML-powered customer intelligence
- Production-grade reliability and scalability
CustomerPulse follows this conceptual flow:
┌──────────────────┐
│ Customer │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Transactions │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Customer Value │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Customer Scoring │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Decision / NBA │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Personalization │
│ & Campaigns │
└──────────────────┘
The long-term goal is to evolve this into:
Customer Data
│
▼
Prediction
│
▼
Decision Engine
│
▼
Next Best Action
│
▼
Personalization / Campaign
│
▼
Measurement & Feedback
│
└──────────────► ML / AI improvement
CustomerPulse starts deliberately as a modular monolith.
The reason is simple:
Build strong domain boundaries first. Distribute the system only when there is a real architectural reason to do so.
The initial architecture follows:
API
│
▼
Application
│
▼
Domain
Infrastructure
│
└── implements persistence / messaging / external integrations
Dependency direction:
API
│
▼
Application
│
▼
Domain
Infrastructure implements the contracts required by the application/domain layers.
This allows the system to evolve toward event-driven microservices without prematurely introducing distributed-system complexity.
- Python 3.12
- FastAPI
- Pydantic
- SQLAlchemy 2.x (async)
- asyncpg
- PostgreSQL 17
- Alembic
- asyncio
- pytest
- HTTPX / FastAPI TestClient
- Next.js 16 (App Router)
- React 19
- TypeScript
- Tailwind CSS
- Recharts (charts and visualizations)
- ESLint
- Docker Compose
- Apache Kafka
- MongoDB
- ML/model-serving infrastructure
- Linux
- Jenkins
- SonarQube
- Nexus
- Observability stack
Redis is intentionally not part of the current architecture.
CustomerPulse/
│
├── backend/
│ ├── app/
│ │ ├── api/
│ │ │ └── v1/
│ │ │ ├── customer_360.py
│ │ │ ├── customers.py
│ │ │ ├── health.py
│ │ │ ├── recommendations.py
│ │ │ ├── router.py
│ │ │ ├── scoring.py
│ │ │ └── transactions.py
│ │ ├── application/
│ │ │ ├── common/
│ │ │ ├── customers/
│ │ │ ├── recommendations/
│ │ │ ├── scoring/
│ │ │ ├── transactions/
│ │ │ └── exceptions.py
│ │ ├── config/
│ │ │ └── settings.py
│ │ ├── domain/
│ │ │ ├── customers/
│ │ │ ├── recommendations/
│ │ │ ├── scoring/
│ │ │ ├── transactions/
│ │ │ └── value/
│ │ ├── infrastructure/
│ │ │ └── database/
│ │ │ ├── repositories/
│ │ │ ├── database.py
│ │ │ ├── models.py
│ │ │ └── unit_of_work.py
│ │ ├── schemas/
│ │ │ ├── customer_360.py
│ │ │ ├── customer_intelligence.py
│ │ │ ├── customers.py
│ │ │ └── transactions.py
│ │ └── main.py
│ ├── alembic/
│ │ ├── versions/
│ │ ├── env.py
│ │ └── script.py.mako
│ ├── alembic.ini
│ ├── tests/
│ │ ├── api/
│ │ ├── integration/
│ │ └── unit/
│ └── pyproject.toml
│
├── frontend/
│ ├── src/
│ │ ├── app/
│ │ │ ├── customers/
│ │ │ │ ├── [customerId]/
│ │ │ │ │ ├── 360/page.tsx
│ │ │ │ │ ├── intelligence/page.tsx
│ │ │ │ │ ├── recommendations/page.tsx
│ │ │ │ │ ├── page.tsx
│ │ │ │ │ └── transactions/
│ │ │ │ ├── new/page.tsx
│ │ │ │ └── page.tsx
│ │ │ ├── favicon.ico
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ ├── components/
│ │ │ ├── intelligence/
│ │ │ ├── layout/
│ │ │ ├── transactions/
│ │ │ └── ui/
│ │ ├── services/
│ │ │ └── api/
│ │ └── types/
│ ├── .gitignore
│ ├── eslint.config.mjs
│ ├── next.config.ts
│ ├── package.json
│ ├── postcss.config.mjs
│ ├── pnpm-lock.yaml
│ ├── tsconfig.json
│ └── README.md
│
├── docs/
│ ├── Architecture.md
│ └── Requirements.md
│
├── docker-compose.yml
├── pyproject.toml
└── README.md
- Python 3.12+
- Node.js 20+
- pnpm 9+
- Docker & Docker Compose (for PostgreSQL)
- PostgreSQL 17 (or use the provided docker-compose)
# Start PostgreSQL
docker-compose up -d
# Install dependencies
pip install -e ".[dev]"
# Run database migrations
alembic upgrade head
# Run tests
pytestcd frontend
# Install dependencies
pnpm install
# Run the development server
pnpm devOpen http://localhost:3000 in your browser.
The frontend proxies API requests to http://localhost:8000 by default (configurable via NEXT_PUBLIC_API_BASE_URL).
# Development server (API docs at http://localhost:8000/docs)
uvicorn backend.app.main:app --reloadThe API documentation is available at:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
Status: ✅ COMPLETE
Phase 1 establishes the complete functional and architectural foundation of CustomerPulse.
Implemented:
- FastAPI application
- API versioning
- Application lifecycle
- Health endpoint
- Test infrastructure
Example:
GET /api/v1/health
Implemented:
- Customer domain entity
- Customer creation
- Customer retrieval
- Customer listing
- Pagination
- Search
- Lifecycle filtering
- Duplicate email handling
- Pydantic request/response models
Implemented:
- PostgreSQL persistence
- SQLAlchemy 2.x
- Async database access
- Alembic migrations
- Database constraints
- Repository abstraction
Implemented lifecycle stages:
ACQUISITION
↓
ONBOARDING
↓
ACTIVATION
↓
ENGAGEMENT
↓
GROWTH
↓
RETENTION
↓
WIN_BACK
Lifecycle stages are represented as domain concepts rather than arbitrary strings throughout the business logic.
Implemented:
- Transaction entity
- Transaction status
- Transaction categories
- Customer transactions
- Pagination
- Idempotency keys
- Database uniqueness constraints
Supported transaction statuses:
PENDING
COMPLETED
FAILED
REVERSED
Implemented API-level handling for important domain conditions:
400 / 422 → Invalid input
404 → Resource not found
409 → Duplicate resource
Business logic remains outside the API layer.
Implemented a Unit of Work abstraction:
UnitOfWork
│
├── CustomerRepository
├── TransactionRepository
├── CustomerValueRepository
├── CustomerScoreRepository
└── RecommendationRepository
This allows multiple changes to participate in one database transaction.
CustomerPulse explicitly addresses concurrent transaction requests.
Transaction creation uses an idempotency key:
(customer_id, idempotency_key)
The database enforces uniqueness.
The system was also tested with concurrent requests to ensure that multiple requests with the same idempotency key result in a single transaction.
Implemented Customer Value:
CustomerValue
├── total_spend
└── transaction_count
Completed transactions update the customer's value.
Example:
Transaction: €250
Transactions: 1
Customer Value:
total_spend = €250
transaction_count = 1
Implemented the first rule-based scoring model.
Current formula:
score =
min(
100,
total_spend × 0.5
+
transaction_count × 5
)
Example:
€120 spend
1 transaction
120 × 0.5 + 1 × 5
= 65
The scoring domain is deliberately separated from the recommendation/decision logic.
This allows the future scoring implementation to evolve from:
Rules
↓
Statistical Model
↓
ML Model
↓
AI / Predictive Model
without redesigning the entire platform.
Implemented the first rule-based Next Best Action engine.
Current rules:
WIN_BACK
→ REACTIVATION
Score >= 80
→ LOYALTY_REWARD
RETENTION
→ RETENTION_OFFER
Score >= 60
→ UPSELL
Score >= 40
→ CROSS_SELL
Otherwise
→ NO_ACTION
Each recommendation also contains an explanation/reason.
Example:
Recommendation:
type = UPSELL
reason = Customer has a strong value score.
Implemented:
GET /api/v1/customers/{customer_id}/360
The Customer 360 view combines:
Customer
│
├── Lifecycle
├── Customer Value
├── Customer Score
├── Transactions
└── Recommendations
This creates the foundation for a unified customer intelligence view.
Implemented:
- Customer search
- Lifecycle filtering
- Pagination
- Combined search + filtering
- Input validation
Example:
GET /api/v1/customers?page=1&page_size=20
Implemented API and integration tests covering:
- Customer creation
- Customer retrieval
- Customer search
- Transaction creation
- Transaction idempotency
- Customer value
- Customer scoring
- Recommendations
- Customer 360
- Background scoring
- Concurrent requests
Current test status:
32 passed
0 failed
Implemented an in-process asynchronous background worker.
Current architecture:
TransactionService
│
▼
Transaction committed
│
▼
Scoring Scheduler
│
▼
BackgroundWorker
│
▼
CustomerScoringJob
│
▼
Fresh DB Session
│
▼
CustomerScoringService
The background job deliberately creates its own database session rather than reusing the request-scoped session.
The scoring repository also uses an atomic PostgreSQL UPSERT to make concurrent scoring operations safe:
Request A ──► UPSERT ──► INSERT
Request B ──► UPSERT ──► UPDATE
This prevents the classic:
SELECT
↓
not found
↓
INSERT
race condition.
A Next.js + TypeScript frontend provides the user interface for the platform.
GET / (static)
Displays the customer count and an overview of the customer lifecycle stages.
GET /customers
Lists customers with:
- Search by name/email
- Filtering by lifecycle stage
- Pagination
GET /customers/{customerId}
Shows customer details, lifecycle stage, and links to transactions, 360 view, intelligence, and recommendations.
GET /customers/{customerId}/360
A unified read model combining customer profile, value, score, transactions, and recommendations. Includes:
- KPI cards (total spend, transaction count, customer score)
- Transaction trend chart (recharts)
- Spending by category chart (recharts)
- Recent transactions table
GET /customers/{customerId}/intelligence
Displays customer health, behavioral signals, risk assessment (churn probability), and the decision/next-best-action flow.
GET /customers/{customerId}/recommendations
POST /customers/{customerId}/recommendations/generate
Lists existing recommendations and allows on-demand generation of Next Best Action recommendations.
GET /customers/{customerId}/transactions
POST /customers/{customerId}/transactions/new
Lists customer transactions with pagination and provides a form to record new transactions (with idempotency keys).
POST /customers/new
Form to create a new customer.
- Next.js App Router for file-based routing
- React Server/Client component split for optimal data fetching
- apiClient wrapper using the native
fetchAPI with centralized error handling - TypeScript types mirroring backend schemas (
@/types/) - Tailwind CSS with dark mode support via
ThemeProvider - Component library with reusable UI primitives (
Card,Badge,MetricCard,LoadingState,ErrorState) - Theme-aware styling throughout (light/dark mode toggle)
The frontend consumes the versioned FastAPI APIs under /api/v1/.
At the end of Phase 1, CustomerPulse is a functional customer intelligence backend.
The platform can:
Create Customer
↓
Record Transaction
↓
Calculate Customer Value
↓
Calculate Customer Score
↓
Generate Next Best Action
↓
Expose Customer 360
with asynchronous background scoring, an interactive Next.js frontend, and automated tests.
Phase 1: 🟢 COMPLETE
Status: 🔵 PLANNED / NEXT
Phase 2 moves CustomerPulse from a functional modular monolith toward a production-oriented, event-driven architecture.
The main objective is:
Introduce distributed-system capabilities only where they provide real business or operational value.
First, introduce domain events.
Example:
TransactionCompleted
│
├── Update Customer Value
│
├── Trigger Scoring
│
└── Trigger other interested processes
Instead of directly coupling transaction processing to every downstream operation:
TransactionService
│
├── Scoring
├── Recommendation
├── Analytics
└── Campaign
we move toward:
TransactionService
│
▼
TransactionCompleted
│
├── Scoring Handler
├── Analytics Handler
├── Recommendation Handler
└── Campaign Handler
This is the first major step toward event-driven architecture.
After establishing domain events, introduce Kafka.
Target architecture:
┌───────────────┐
│ Transaction │
│ Service │
└───────┬───────┘
│
▼
┌───────────────┐
│ Kafka │
└───────┬───────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Scoring Analytics Recommendation
Consumer Consumer Consumer
Topics, consumer groups, partitioning, ordering, retries and dead-letter handling will be introduced progressively.
Phase 2 will address:
- At-least-once delivery
- Idempotent consumers
- Retry policies
- Dead Letter Queue
- Poison messages
- Event ordering
- Correlation IDs
- Message tracing
- Consumer failures
The goal is to understand the difference between:
Message delivered
and:
Business operation successfully processed
The current CustomerValue implementation uses a read-modify-write pattern.
Under heavy concurrent transaction processing, this can potentially cause lost updates.
Phase 2 will introduce a stronger approach, such as:
Atomic SQL update
or appropriate row-level concurrency control.
The objective is to guarantee correct customer value even under high transaction concurrency.
Introduce a dedicated prediction abstraction.
Architecture:
Customer Data
│
▼
Feature Preparation
│
▼
Prediction Model
│
▼
Prediction Result
Prediction remains separate from business decisions.
For example:
Prediction:
churn_probability = 0.87
does not automatically mean:
Decision:
send_discount
The Decision Engine remains responsible for turning predictions into actions.
Introduce an explicit decision layer:
Customer Data
│
▼
Predictions
│
▼
Business Rules
│
▼
Constraints
│
▼
Decision
Example:
Churn probability = 0.87
Customer value = HIGH
Lifecycle = RETENTION
Campaign eligibility = TRUE
↓
Decision:
RETENTION_OFFER
This separation is particularly important for explainability and enterprise systems.
The current NBA engine is rule-based.
It will evolve toward:
Rule-based NBA
↓
Scored candidates
↓
Prediction-assisted NBA
↓
Optimization
↓
AI-assisted decisioning
Potential inputs:
- Customer value
- Lifecycle stage
- Churn probability
- Purchase history
- Product affinity
- Customer behavior
- Campaign history
- Business constraints
MongoDB will be introduced where document-oriented data provides a genuine advantage.
Potential use cases:
- Customer behavioral profiles
- Flexible customer attributes
- Model/prediction metadata
- Recommendation context
- Event-derived customer views
PostgreSQL remains the source of truth for transactional relational data.
Introduce production-grade observability:
Logs
Metrics
Traces
Health Checks
Correlation IDs
Important metrics include:
TPS
P95 latency
P99 latency
Error rate
Queue depth
Consumer lag
Job processing time
Database latency
This allows architecture decisions to be based on measurable system behavior rather than assumptions.
Phase 2 will address:
- Timeouts
- Retries
- Backoff
- Circuit breakers where appropriate
- Idempotency
- Failure isolation
- Graceful shutdown
- Background worker draining
- Database failure scenarios
- Kafka failure scenarios
Introduce:
- Authentication
- Authorization
- Role-based access
- API security
- Input validation
- Secrets management
- Secure configuration
- Audit logging
The project will also be used to deeply explore modern Python backend engineering.
Topics include:
async / await
asyncio
Tasks
Queues
I/O concurrency
Cancellation
Timeouts
Context management
Generators
Async generators
Dependency injection
Typing
Protocols
Generics
Performance optimization
The goal is not learning Python syntax in isolation.
The goal is understanding how Python is used to build high-performance asynchronous backend systems.
CustomerPulse will eventually run as containerized services:
Frontend
│
Backend API
│
┌─┴───────────────┐
│ │
PostgreSQL Kafka
│ │
└───────┬─────────┘
│
Background
Consumers
Topics include:
- Multi-stage Docker builds
- Container security
- Linux runtime behavior
- Resource limits
- Health checks
- Networking
- Production configuration
Planned pipeline:
Git Push
│
▼
Build
│
▼
Unit Tests
│
▼
Integration Tests
│
▼
Static Analysis
│
▼
Security Checks
│
▼
Docker Build
│
▼
Artifact Repository
│
▼
Deployment
Potential tooling:
- Jenkins
- SonarQube
- Nexus
- Docker
- Kubernetes
A Next.js + TypeScript frontend provides the user interface:
Customer Dashboard
│
├── Customer 360
├── Lifecycle
├── Transactions
├── Customer Value
├── Score
├── Intelligence
├── Recommendations
└── Next Best Actions
The frontend consumes the versioned FastAPI APIs under /api/v1/.
The overall evolution of CustomerPulse is intentional:
PHASE 1
Modular Monolith
│
├── PostgreSQL
├── REST API
├── Domain Layer
├── Application Layer
└── Background Worker
↓
PHASE 2
Event-Driven Modular System
│
├── REST API
├── Domain Events
├── Kafka
├── Consumers
├── PostgreSQL
├── MongoDB
├── Prediction
├── Decision Engine
└── Observability
↓
FUTURE
Distributed Customer Intelligence Platform
│
├── Customer Data
├── Event Streaming
├── ML / AI
├── Prediction
├── Decisioning
├── Next Best Action
├── Personalization
├── Experimentation
└── Continuous Feedback
CustomerPulse follows several principles throughout its evolution.
Business concepts should not depend on infrastructure.
Customer, transaction, scoring, recommendation and prediction responsibilities should remain clearly separated.
Database constraints and atomic operations are used where correctness depends on concurrent access.
Asynchronous programming is used for I/O-bound and background workloads rather than simply making everything async.
Events should be introduced when multiple independent consumers need to react to business events.
Do not introduce Kafka, microservices or other distributed infrastructure simply because they are fashionable.
Performance decisions should be based on:
TPS
P95
P99
CPU
Memory
Database latency
Queue depth
Consumer lag
Phase 1 — Core Customer Intelligence
████████████████████████████████ 100%
Phase 2 — Event-Driven & Production Architecture
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0%
Current test status:
32 passed
0 failed
Current APIs include:
GET /api/v1/health
GET /api/v1/customers
POST /api/v1/customers
GET /api/v1/customers/{customer_id}
GET /api/v1/customers/{customer_id}/intelligence
GET /api/v1/transactions/customers/{customer_id}
POST /api/v1/transactions/customers/{customer_id}
GET /api/v1/transactions/{transaction_id}
GET /api/v1/customers/{customer_id}/scores
GET /api/v1/customers/{customer_id}/recommendations
POST /api/v1/customers/{customer_id}/recommendations/generate
GET /api/v1/customers/{customer_id}/360
CustomerPulse is also an engineering learning project.
The objective is to practice the complete lifecycle of a modern backend platform:
Requirements
↓
Domain Modeling
↓
Architecture
↓
Implementation
↓
Testing
↓
Concurrency
↓
Async Processing
↓
Event-Driven Architecture
↓
Distributed Systems
↓
AI / ML
↓
Observability
↓
CI/CD
↓
Production
The final system should demonstrate not only that the application works, but why the architecture works, where it can fail, how it scales, and how it should evolve.
A phase is considered complete when:
- The required functionality is implemented.
- Domain boundaries are respected.
- Tests cover the important behavior.
- Concurrency implications are understood.
- Architecture decisions are documented.
- Known technical debt is explicitly identified rather than hidden.
This project is currently a personal engineering and learning project.