Skip to content

Customer Management Context ​

CRM functionality for customer relationships and sales pipeline


Overview ​

The Customer Management Context handles all CRM (Customer Relationship Management) functionality. This is where business relationships are managed, distinct from technical user authentication (Identity context).

Key Concepts ​

  • Customer - B2C customer or B2B contact person
  • Company - B2B organization management (planned)
  • Deal - Sales opportunity tracking (planned)
  • Interaction - Customer interaction history (planned)

Status ​

AggregateStatusDescription
CustomerProductionCore customer lifecycle
CompanyPlannedB2B organizations
DealPlannedSales pipeline
InteractionPlannedCalls, emails, meetings, notes

Customer Aggregate (Production) ​

Overview ​

The Customer aggregate manages the complete customer lifecycle from lead to churned customer, with support for both B2C and B2B scenarios.

Entity Structure ​

go
type Customer struct {
    aggregate.BaseAggregate

    // Identity
    ID         uuidv7.UUID
    UserID     *uuidv7.UUID // optional link to Identity.User
    CompanyID  *uuidv7.UUID // optional (B2B) or nil (B2C)

    // Basic Info
    Name       string
    Email      valueobject.Email
    Phone      *valueobject.Phone

    // Status
    Status     CustomerStatus // lead, prospect, customer, churned
    Tier       CustomerTier   // free, basic, pro, enterprise
    Source     string         // website, referral, cold-call, event

    // Relationship
    AssignedTo uuidv7.UUID    // Sales rep (Identity.User)
    Tags       []string       // marketing, vip, high-value

    // Lifecycle
    CreatedAt  time.Time
    UpdatedAt  time.Time
    ChurnedAt  *time.Time
}

Customer Status (Lifecycle) ​

   QualifyAsProspect      ConvertToCustomer   
 Lead  >  Prospect  >  Customer 
                                              
                                                                         
                                                                          Churn
                                                                         v
                                                                   
                                                                    Churned 
                                                                   
                                                                         
                                                                          Reactivate
                                                                         v
                                                                   
                                                                    Customer

Customer Tiers ​

TierDescriptionTypical Use Case
freeFree tier customerTrial users, freemium
basicBasic paid planSmall businesses, startups
proProfessional planGrowing businesses
enterpriseEnterprise planLarge organizations

Business Rules ​

  1. Email uniqueness:

    • B2C: Email must be globally unique
    • B2B: Email must be unique within company
  2. Mutual exclusivity: Customer cannot have both UserID and CompanyID

    • UserID → B2C customer (individual user)
    • CompanyID → B2B contact person
  3. Status transitions (one-way):

    • Lead → Prospect → Customer → Churned
    • Churned → Customer (reactivation allowed)
    • Cannot go backwards (e.g., Customer → Prospect)
  4. Assignment: Only one assigned sales rep at a time

  5. Tier upgrades: Can only upgrade tier (not downgrade)

Key Methods ​

go
// Status transitions
func (c *Customer) QualifyAsProspect() error
func (c *Customer) ConvertToCustomer() error
func (c *Customer) Churn(reason string) error
func (c *Customer) Reactivate() error

// Management
func (c *Customer) AssignToRep(repID uuidv7.UUID) error
func (c *Customer) UpgradeTier(newTier CustomerTier) error
func (c *Customer) AddTag(tag string) error
func (c *Customer) RemoveTag(tag string) error

Database Schema ​

Customers Table ​

sql
CREATE TABLE customer_mgmt_customers (
    id UUID PRIMARY KEY DEFAULT uuid_v7(),
    user_id UUID REFERENCES identity_users(id),
    company_id UUID REFERENCES customer_mgmt_companies(id),
    
    -- Basic Info
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL,
    phone VARCHAR(50),
    
    -- Status
    status VARCHAR(50) NOT NULL,
    tier VARCHAR(50) NOT NULL,
    source VARCHAR(100),
    
    -- Relationship
    assigned_to UUID REFERENCES identity_users(id),
    tags TEXT[],
    
    -- Lifecycle
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    churned_at TIMESTAMP,
    deleted_at TIMESTAMP,
    
    -- Constraints
    CONSTRAINT unique_email_per_company UNIQUE (email, company_id)
);

-- Indexes
CREATE INDEX idx_customers_status ON customer_mgmt_customers(status);
CREATE INDEX idx_customers_tier ON customer_mgmt_customers(tier);
CREATE INDEX idx_customers_assigned_to ON customer_mgmt_customers(assigned_to);
CREATE INDEX idx_customers_user_id ON customer_mgmt_customers(user_id);
CREATE INDEX idx_customers_company_id ON customer_mgmt_customers(company_id);

API Endpoints ​

Customer Management ​

Base URL: /api/v1/customer-mgmt

Create Customer ​

http
POST /customers
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "John Doe",
  "email": "john.doe@example.com",
  "phone": "+441234567890",
  "status": "lead",
  "tier": "free",
  "source": "website",
  "tags": ["marketing", "newsletter"]
}

Response (201 Created):

json
{
  "status": "success",
  "data": {
    "id": "01JGABC1234567890DEFGHIJK",
    "name": "John Doe",
    "email": "john.doe@example.com",
    "phone": "+441234567890",
    "status": "lead",
    "tier": "free",
    "source": "website",
    "tags": ["marketing", "newsletter"],
    "assigned_to": null,
    "created_at": "2025-12-29T12:00:00Z",
    "updated_at": "2025-12-29T12:00:00Z"
  }
}

List Customers ​

http
GET /customers?status=lead&tier=free&page=1&page_size=20
Authorization: Bearer <token>

Query Parameters:

  • status - Filter by customer status (lead, prospect, customer, churned)
  • tier - Filter by customer tier (free, basic, pro, enterprise)
  • assigned_to - Filter by assigned sales rep ID
  • page - Page number (default: 1)
  • page_size - Items per page (default: 20, max: 100)

Response (200 OK):

json
{
  "status": "success",
  "data": [
    {
      "id": "01JGABC1234567890DEFGHIJK",
      "name": "John Doe",
      "email": "john.doe@example.com",
      "status": "lead",
      "tier": "free"
    }
  ],
  "pagination": {
    "total": 150,
    "page": 1,
    "page_size": 20,
    "total_pages": 8
  }
}

Get Customer by ID ​

http
GET /customers/:id
Authorization: Bearer <token>

Response (200 OK):

json
{
  "status": "success",
  "data": {
    "id": "01JGABC1234567890DEFGHIJK",
    "name": "John Doe",
    "email": "john.doe@example.com",
    "phone": "+441234567890",
    "status": "lead",
    "tier": "free",
    "source": "website",
    "tags": ["marketing", "newsletter"],
    "assigned_to": null,
    "created_at": "2025-12-29T12:00:00Z",
    "updated_at": "2025-12-29T12:00:00Z"
  }
}

Update Customer ​

http
PUT /customers/:id
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "John Doe Updated",
  "phone": "+441234567890",
  "tags": ["vip", "high-value"]
}

Response (200 OK):

json
{
  "status": "success",
  "data": {
    "id": "01JGABC1234567890DEFGHIJK",
    "name": "John Doe Updated",
    "phone": "+441234567890",
    "tags": ["vip", "high-value"],
    "updated_at": "2025-12-29T12:05:00Z"
  }
}

Qualify as Prospect ​

http
POST /customers/:id/qualify
Authorization: Bearer <token>

Response (200 OK):

json
{
  "status": "success",
  "data": {
    "id": "01JGABC1234567890DEFGHIJK",
    "status": "prospect",
    "updated_at": "2025-12-29T12:10:00Z"
  }
}

Convert to Customer ​

http
POST /customers/:id/convert
Authorization: Bearer <token>

Response (200 OK):

json
{
  "status": "success",
  "data": {
    "id": "01JGABC1234567890DEFGHIJK",
    "status": "customer",
    "updated_at": "2025-12-29T12:15:00Z"
  }
}

Churn Customer ​

http
POST /customers/:id/churn
Authorization: Bearer <token>
Content-Type: application/json

{
  "reason": "Price too high"
}

Response (200 OK):

json
{
  "status": "success",
  "data": {
    "id": "01JGABC1234567890DEFGHIJK",
    "status": "churned",
    "churned_at": "2025-12-29T12:20:00Z"
  }
}

Assign Sales Rep ​

http
POST /customers/:id/assign
Authorization: Bearer <token>
Content-Type: application/json

{
  "assigned_to": "01JGXYZ9876543210ABCDEFGH"
}

Response (200 OK):

json
{
  "status": "success",
  "data": {
    "id": "01JGABC1234567890DEFGHIJK",
    "assigned_to": "01JGXYZ9876543210ABCDEFGH",
    "updated_at": "2025-12-29T12:25:00Z"
  }
}

Usage Examples ​

Create and Qualify Lead ​

bash
# 1. Create lead
CUSTOMER_ID=$(curl -s -X POST http://localhost:8081/api/v1/customer-mgmt/customers \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Alice Smith",
    "email": "alice@startup.com",
    "phone": "+441234567890",
    "status": "lead",
    "tier": "free",
    "source": "website",
    "tags": ["marketing"]
  }' | jq -r '.data.id')

# 2. Qualify as prospect
curl -X POST http://localhost:8081/api/v1/customer-mgmt/customers/$CUSTOMER_ID/qualify \
  -H "Authorization: Bearer $TOKEN"

# 3. Assign to sales rep
curl -X POST http://localhost:8081/api/v1/customer-mgmt/customers/$CUSTOMER_ID/assign \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"assigned_to":"01JGREP9876543210ABCDEFGH"}'

# 4. Convert to customer
curl -X POST http://localhost:8081/api/v1/customer-mgmt/customers/$CUSTOMER_ID/convert \
  -H "Authorization: Bearer $TOKEN"
bash
# Get all leads assigned to me
curl "http://localhost:8081/api/v1/customer-mgmt/customers?status=lead&assigned_to=$MY_USER_ID" \
  -H "Authorization: Bearer $TOKEN"

# Get all enterprise customers
curl "http://localhost:8081/api/v1/customer-mgmt/customers?tier=enterprise" \
  -H "Authorization: Bearer $TOKEN"

# Get VIP customers
curl "http://localhost:8081/api/v1/customer-mgmt/customers?tags=vip" \
  -H "Authorization: Bearer $TOKEN"

Domain Events ​

Customer aggregate publishes events for asynchronous communication:

go
// Customer created
type CustomerCreatedEvent struct {
    CustomerID uuidv7.UUID
    Status     CustomerStatus
    Email      string
    Timestamp  time.Time
}

// Status changed
type CustomerStatusChangedEvent struct {
    CustomerID uuidv7.UUID
    FromStatus CustomerStatus
    ToStatus   CustomerStatus
    Timestamp  time.Time
}

// Customer converted
type CustomerConvertedEvent struct {
    CustomerID uuidv7.UUID
    Timestamp  time.Time
}

// Customer churned
type CustomerChurnedEvent struct {
    CustomerID uuidv7.UUID
    Reason     string
    Timestamp  time.Time
}

// Tier upgraded
type CustomerTierUpgradedEvent struct {
    CustomerID uuidv7.UUID
    FromTier   CustomerTier
    ToTier     CustomerTier
    Timestamp  time.Time
}

Testing ​

Test Statistics ​

TypeTestsCoverageDescription
Unit2590%Customer entity logic
Smoke18-HTTP handlers (mock-based)
Integration14-Repository with real DB
Total5785%Comprehensive test coverage

Run Tests ​

bash
# All customer tests
go test ./internal/contexts/customer-mgmt/... -v

# Smoke tests
go test ./test/smoke/contexts/customer-mgmt/... -v

# Integration tests
go test ./test/integration/contexts/customer-mgmt/... -v

Communication with Other Contexts ​

With Identity Context ​

go
// Subscribe to user registration
eventBus.Subscribe("identity.user.registered", func(event UserRegisteredEvent) {
    // Create customer profile for new user
    customerService.CreateCustomerFromUser(event.UserID, event.Email)
})

With Order Management (Planned) ​

go
// Publish customer converted event
eventBus.Publish("customer.converted", CustomerConvertedEvent{...})

// Order Management creates initial contract
eventBus.Subscribe("customer.converted", func(event CustomerConvertedEvent) {
    orderService.CreateWelcomeContract(event.CustomerID)
})

With Billing (Planned) ​

go
// Publish tier upgrade event
eventBus.Publish("customer.tier_upgraded", CustomerTierUpgradedEvent{...})

// Billing updates subscription
eventBus.Subscribe("customer.tier_upgraded", func(event CustomerTierUpgradedEvent) {
    billingService.UpgradeSubscription(event.CustomerID, event.ToTier)
})

Planned Features ​

Company Aggregate (Q1 2026) ​

  • B2B organization management
  • Multiple contacts per company
  • Account manager assignment
  • Company-level deals and interactions

Deal Aggregate (Q1 2026) ​

  • Sales opportunity tracking
  • Pipeline stages (discovery, proposal, negotiation, closing)
  • Deal value and probability
  • Won/lost tracking

Interaction Aggregate (Q2 2026) ​

  • Call logging
  • Email tracking
  • Meeting notes
  • Customer interaction history

Best Practices ​

Customer Lifecycle Management ​

  1. Lead qualification: Verify email and phone before qualifying
  2. Proper assignment: Assign sales rep based on territory or workload
  3. Status tracking: Always use proper state transitions (no shortcuts)
  4. Churn handling: Record churn reason for analytics
  5. Reactivation: Verify customer details before reactivation

Performance Optimization ​

  1. Indexes: Ensure indexes on status, tier, assigned_to columns
  2. Pagination: Always use pagination for list endpoints (max 100 items)
  3. Caching: Cache customer counts by status/tier (refresh every 5 minutes)
  4. Search: Use database full-text search for customer name/email lookups

Security ​

  1. Authorization: Verify user has permission to manage customers
  2. Soft delete: Never hard-delete customers (use deleted_at)
  3. Audit trail: Log all status changes and assignments
  4. Data privacy: Implement GDPR-compliant data export/deletion

Next Steps ​

Built with Domain-Driven Design and Go