| name | Contact Management |
| description | Organizing and tracking interactions with customers and prospects including data models, segmentation, enrichment, GDPR compliance, and contact lifecycle management. |
Contact Management
Current Level: Intermediate
Domain: CRM / Sales
Overview
Contact management organizes and tracks interactions with customers and prospects. This guide covers data models, segmentation, enrichment, and GDPR compliance for managing customer relationships effectively.
Contact Data Model
CREATE TABLE contacts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
phone VARCHAR(50),
mobile VARCHAR(50),
title VARCHAR(100),
department VARCHAR(100),
company_id UUID REFERENCES companies(id),
address_line1 VARCHAR(255),
address_line2 VARCHAR(255),
city VARCHAR(100),
state VARCHAR(100),
postal_code VARCHAR(20),
country VARCHAR(100),
lifecycle_stage VARCHAR(50),
lead_status VARCHAR(50),
lead_source VARCHAR(50),
owner_id UUID REFERENCES users(id),
linkedin_url VARCHAR(255),
twitter_handle VARCHAR(100),
last_contacted_at TIMESTAMP,
last_activity_at TIMESTAMP,
email_opt_in BOOLEAN DEFAULT TRUE,
consent_given BOOLEAN DEFAULT FALSE,
consent_date TIMESTAMP,
data_processing_consent BOOLEAN DEFAULT FALSE,
custom_fields JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
deleted_at TIMESTAMP,
INDEX idx_email (email),
INDEX idx_company (company_id),
INDEX idx_owner (owner_id),
INDEX idx_lifecycle (lifecycle_stage),
FULLTEXT idx_search (first_name, last_name, email)
);
CREATE TABLE contact_tags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) UNIQUE NOT NULL,
color VARCHAR(7),
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE contact_tag_relations (
contact_id UUID REFERENCES contacts(id) ON DELETE CASCADE,
tag_id UUID REFERENCES contact_tags(id) ON DELETE CASCADE,
created_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (contact_id, tag_id)
);
CREATE TABLE contact_lists (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
description TEXT,
type VARCHAR(50) DEFAULT 'static',
filters JSONB,
created_by UUID REFERENCES users(id),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE contact_list_members (
list_id UUID REFERENCES contact_lists(id) ON DELETE CASCADE,
contact_id UUID REFERENCES contacts(id) ON DELETE CASCADE,
added_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (list_id, contact_id)
);
Contact Segmentation
export class ContactSegmentationService {
async createStaticList(name: string, contactIds: string[]): Promise<ContactList> {
const list = await db.contactList.create({
data: {
name,
type: 'static'
}
});
await db.contactListMember.createMany({
data: contactIds.map(contactId => ({
listId: list.id,
contactId
}))
});
return list;
}
async createDynamicList(name: string, filters: ListFilter[]): Promise<ContactList> {
const list = await db.contactList.create({
data: {
name,
type: 'dynamic',
filters: JSON.stringify(filters)
}
});
.(list.);
list;
}
(: ): <> {
list = db..({
: { : listId }
});
(!list || list. !== ) {
();
}
filters = .(list. ) [];
contacts = .(filters);
db..({
: { listId }
});
db..({
: contacts.( ({
listId,
: contact.
}))
});
}
(: []): <[]> {
: = {};
filters.( {
(filter.) {
:
where[filter.] = filter.;
;
:
where[filter.] = { : filter. };
;
:
where[filter.] = { : filter. };
;
:
where[filter.] = { : filter. };
;
}
});
db..({ where });
}
}
{
: ;
: | | | ;
: ;
}
Contact Enrichment
import axios from 'axios';
export class ContactEnrichmentService {
async enrichContact(contactId: string): Promise<Contact> {
const contact = await db.contact.findUnique({
where: { id: contactId }
});
if (!contact) throw new Error('Contact not found');
const enrichedData = await this.enrichWithClearbit(contact.email);
return db.contact.update({
where: { id: contactId },
data: {
title: enrichedData.title || contact.title,
linkedinUrl: enrichedData.linkedin || contact.linkedinUrl,
twitterHandle: enrichedData.twitter || contact.twitterHandle,
customFields: {
...contact.customFields,
...enrichedData.customFields
}
}
});
}
(: ): <> {
{
response = axios.(
,
{
: {
:
}
}
);
person = response..;
company = response..;
{
: person.?.,
: person.?.,
: person.?.,
: {
: person.,
: company?.,
: company?.,
: company?.?.
}
};
} (error) {
.(, error);
{};
}
}
}
{
?: ;
?: ;
?: ;
?: <, >;
}
Duplicate Management
export class ContactDuplicateService {
async findDuplicates(contactId: string): Promise<Contact[]> {
const contact = await db.contact.findUnique({
where: { id: contactId }
});
if (!contact) return [];
const emailDuplicates = await db.contact.findMany({
where: {
email: contact.email,
id: { not: contactId }
}
});
const nameDuplicates = await db.contact.findMany({
where: {
firstName: contact.firstName,
lastName: contact.lastName,
companyId: contact.companyId,
id: { not: contactId }
}
});
const allDuplicates = [...emailDuplicates, ...nameDuplicates];
return Array.from(new (allDuplicates.( [c., c])).());
}
(: , : []): <> {
primary = db..({
: { : primaryId }
});
(!primary) ();
duplicates = db..({
: { : { : duplicateIds } }
});
: = { ...primary };
duplicates.( {
.(duplicate).( {
(duplicate[key] && !merged[key]) {
merged[key] = duplicate[key];
}
});
});
updated = db..({
: { : primaryId },
: merged
});
.(duplicateIds, primaryId);
db..({
: { : { : duplicateIds } },
: { : () }
});
updated;
}
(: [], : ): <> {
.([
db..({
: { : { : fromIds } },
: { : toId }
}),
db..({
: { : { : fromIds } },
: { : toId }
}),
db..({
: { : { : fromIds } },
: { : toId }
})
]);
}
}
Bulk Operations
export class ContactBulkService {
async bulkImport(contacts: ImportContactDto[]): Promise<BulkImportResult> {
const results: BulkImportResult = {
success: 0,
failed: 0,
errors: []
};
for (const contactData of contacts) {
try {
this.validateContact(contactData);
const existing = await db.contact.findUnique({
where: { email: contactData.email }
});
if (existing) {
await db.contact.update({
where: { id: existing.id },
data: contactData
});
} else {
await db.contact.create({
data: contactData
});
}
results.++;
} (error) {
results.++;
results..({
: contactData.,
: error.
});
}
}
results;
}
(?: ): <[]> {
db..({
: filters,
: {
: ,
:
}
});
}
(: [], : <>): <> {
result = db..({
: { : { : contactIds } },
: updates
});
result.;
}
(: []): <> {
result = db..({
: { : { : contactIds } },
: { : () }
});
result.;
}
(: ): {
(!contact.) {
();
}
(!.(contact.)) {
();
}
}
(: ): {
emailRegex = ;
emailRegex.(email);
}
}
{
: ;
: ;
: ;
?: ;
?: ;
}
{
: ;
: ;
: <{ : ; : }>;
}
GDPR Compliance
export class ContactGDPRService {
async recordConsent(contactId: string, consentType: string): Promise<void> {
await db.contact.update({
where: { id: contactId },
data: {
consentGiven: true,
consentDate: new Date(),
dataProcessingConsent: consentType === 'full'
}
});
await db.consentLog.create({
data: {
contactId,
consentType,
timestamp: new Date()
}
});
}
async exportContactData(contactId: string): Promise<ContactDataExport> {
const contact = await db.contact.findUnique({
where: { id: contactId },
include: {
activities: true,
tasks: true,
:
}
});
(!contact) ();
{
: contact,
: contact.,
: contact.,
: contact.
};
}
(: ): <> {
db..({
: { : contactId },
: {
: ,
: ,
: ,
: ,
: ,
: ()
}
});
}
}
{
: ;
: [];
: [];
: [];
}
Best Practices
- Data Quality - Validate and clean data
- Deduplication - Prevent and merge duplicates
- Enrichment - Enrich contacts automatically
- Segmentation - Create targeted segments
- GDPR - Comply with data regulations
- Bulk Operations - Support import/export
- Search - Implement full-text search
- Tags - Use tags for organization
- Lists - Support static and dynamic lists
- Audit Trail - Track all changes
Quick Start
Contact Model
interface Contact {
id: string
firstName: string
lastName: string
email: string
phone?: string
company?: string
tags: string[]
customFields: Record<string, any>
createdAt: Date
updatedAt: Date
}
async function createContact(contact: Contact) {
const existing = await findDuplicateContact(contact.email)
if (existing) {
return await mergeContacts(existing.id, contact)
}
return await db.contacts.create({ data: contact })
}
Contact Segmentation
async function segmentContacts(): Promise<ContactSegment[]> {
const contacts = await db.contacts.findMany()
return {
highValue: contacts.filter(c => c.totalRevenue > 10000),
active: contacts.filter(c => c.lastContactDate > subDays(new Date(), 30)),
inactive: contacts.filter(c => c.lastContactDate < subDays(new Date(), 90))
}
}
Production Checklist
Anti-patterns
❌ Don't: No Deduplication
await db.contacts.create({ data: contact })
const existing = await findDuplicateContact(contact.email)
if (existing) {
await mergeContacts(existing.id, contact)
} else {
await db.contacts.create({ data: contact })
}
❌ Don't: Ignore GDPR
await db.contacts.create({ data: contact })
if (contact.consentGiven) {
await db.contacts.create({ data: contact })
await trackConsent(contact.id, 'data_processing')
}
Integration Points
- Lead Management (
32-crm-integration/lead-management/) - Lead to contact
- Salesforce Integration (
32-crm-integration/salesforce-integration/) - CRM sync
- Marketing Automation (
28-marketing-integration/marketing-automation/) - Marketing
Further Reading
Resources