CRM with CSV import architecture
CRM with CSV import architecture: the decisions that matter
The architecture of a CRM with CSV import comes down to 3 choices: chunked import transactions, not one giant transaction, normalised email as the contact identity and erasure tombstones, audit stays. Each is explained below with the alternatives that were rejected and what you accept by choosing it.
Last checked
Chunked import transactions, not one giant transaction
Situation: A 100k-row import in one transaction holds locks and loses everything on one failure.
Choice: Apply rows in chunks of at most 500, each in its own transaction, tracking per-row status.
- Rejected: One transaction per file: long locks and all-or-nothing.
- Rejected: Row-by-row autocommit: slow and no atomic chunk.
- Consequence: A crash resumes from stored row statuses.
- Consequence: Partial imports are visible and undoable.
- Consequence: Undo relies on import_job_id tagging.
Normalised email as the contact identity
Situation: Imports and manual entry produce differently cased or spaced emails.
Choice: Store email_normalized with a unique index per workspace and merge on conflict by policy.
- Rejected: Match on raw email: duplicates slip through.
- Rejected: Fuzzy matching on name: false merges.
- Consequence: Re-uploads are idempotent.
- Consequence: Contacts without email need another key.
- Consequence: Normalisation rules must be documented and tested.
Erasure tombstones, audit stays
Situation: Privacy law requires erasure but the audit trail must remain intact.
Choice: Tombstone personal columns and redact personal fields in audit payloads without deleting audit rows.
- Rejected: Delete everything including audit: loses accountability.
- Rejected: Keep everything: violates erasure requests.
- Consequence: Audit shows that an erasure happened and who did it.
- Consequence: Redaction code must cover every payload shape.
- Consequence: Backups need a retention policy.
How the data is indexed
Indexes are chosen from the questions the app asks most often. Each line below names a table and the question its main index answers.
- workspaces: unique (slug) — workspace lookup.
- contacts: unique (workspace_id, email_normalized) — de-duplication key.
- companies: unique (workspace_id, domain) — company de-duplication.
- deals: (workspace_id, stage_id, id) — kanban column pages.
- import_jobs: unique (workspace_id, file_hash) — re-upload of the same file is a no-op.
- import_rows: unique (import_job_id, row_no) — one result per row.
- record_versions: unique (entity, entity_id, version) — one row per version.
- audit_log: (workspace_id, created_at DESC) — audit viewer, keyset paginated.
What Forge64 provides underneath
Forge64 matches your description to one of 25 reference architectures, writes the plan and data model into the project, generates the app, and runs a quality gate over the code before you see it. Each app gets its own Postgres database and sign-in with no per-user fee. Hosting on Forge Cloud and Forge Payments are rolling out.
This is a reference design for the category, not a promise that every item is generated today. Built-ins such as background jobs, file uploads and search are rolling out; email sent from your app is coming soon, so anything here that needs it is something you add yourself for now.
FAQ
- What is the most important architecture decision for a CRM with CSV import?
- Chunked import transactions, not one giant transaction. Apply rows in chunks of at most 500, each in its own transaction, tracking per-row status.
- Do I have to make these decisions myself?
- No. Forge AI plans them from your description and writes the decisions into the project as architecture records you can read and change.
- What do the alternatives cost me?
- For example, for "Normalised email as the contact identity": Match on raw email: duplicates slip through. Fuzzy matching on name: false merges.
Build your app with Forge64
One prompt to a running app with auth, database, hosting and payments. Start free, no credit card required.
Start building for free