Architecture of a custom CRM system

22.06.202510 min read
Makar Kucherenko
Python developerMakar Kucherenko

CRM architecture determines more than just interface speed. It determines whether the system can safely store client data, survive an increase in load, connect new sales channels and change along with the company’s processes.

In this article, we will analyze the technical side: how to separate frontend and backend CRM, when to choose Node.js, where you need a message broker and how to connect 1C, website, telephony and external APIs without duplicates and data loss. If you need an assessment of the finished project, and not just the choice of technologies, see the conditions development of a custom CRM system.

Short answer: for most CRMs, a reasonable starting architecture remains a modular monolith with PostgreSQL, a documented API, and a separate background task queue. Integrations need to be designed before the interface is developed: determine the owner of each field, entity IDs, re-delivery rules, and how to restore the exchange after a failure.

Where does CRM architecture begin?

Choosing a framework should not be the first decision. First the command commits:

  • user roles and access restrictions;
  • main entities: lead, client, deal, object, order, invoice, document;
  • the life cycle of each entity and acceptable transitions between statuses;
  • automatic actions, notifications and background tasks;
  • data sources and external systems;
  • response time, availability, and failure recovery requirements;
  • forecast for the number of users, records and operations.

This data is turned into a domain model and integration map. The detailed procedure for collecting requirements is described in CRM design guide.

Modular monolith or microservice CRM

Modular monolith

For a first CRM release, a modular monolith is often more practical than microservices. The system is deployed as a single application, but the code is divided into independent modules: clients, transactions, tasks, documents, notifications, analytics and integrations.

Advantages of the approach:

  • easier development, testing and deployment;
  • transactions between related entities remain predictable;
  • less infrastructure costs;
  • faster release of MVP and testing of processes on real users.

The main condition is not to mix modules through common tables and random calls. Each module should have clear boundaries and public interfaces.

Microservices

Microservice architecture makes sense when parts of the system have different workloads, are produced by independent teams, or require isolation. For example, call processing, document generation, import of large feeds and BI analytics can be scaled separately from the main CRM.

But independence comes at a price:

  • distributed tracing and complex diagnostics appear;
  • data between services does not become consistent instantly;
  • we need queues, re-delivery of events and protection against duplicates;
  • DevOps, test environments, and API version control become more complex.

Therefore, it is better to associate the transition to microservices with a measurable problem, and not make it a mandatory requirement of any CRM project.

Backend CRM: Node.js, Python, PHP or Go

The backend language is chosen based on workload, team competencies, and integration ecosystem.

Node.js and TypeScript

Node.js is suitable for CRM with a large number of network operations: API, WebSocket, telephony, chats, notifications and exchange with external services. TypeScript helps formalize data contracts between frontend and backend. For a structured application, NestJS is often used, for compact services - Fastify or Express.

Python

Python is convenient when CRM is closely related to analytics, document processing, machine learning or AI modules. Django provides a ready-made administrative part and ORM, FastAPI is suitable for APIs and individual services.

PHP

Laravel and Symfony remain a rational choice for business systems, especially if the company already has a PHP team or integration with the site and 1C-Bitrix. The quality of the architecture does not depend on the language, but on the boundaries of the modules, the data model, the tests and the development process.

Go

Go is suitable for services with high concurrency, threading, and stringent resource consumption requirements. It is not necessary to use it for the entire CRM: often it is enough to allocate one loaded component to Go.

Database and storage model

For the basic transactional model of CRM, PostgreSQL is usually suitable. The relational database provides integrity constraints, transactions, indexes, full-text search and work with JSON fields.

Additional storages are connected for a specific task:

  • Redis - cache, locks, temporary data and small-scale queues;
  • OpenSearch or Elasticsearch - complex search across a large database;
  • ClickHouse - events and analytical sections;
  • S3-compatible storage - documents, call recordings and attachments;
  • vector database - semantic search and RAG based on company documents.

You should not transfer all data to specialized storage facilities. The customer card, transaction and payment must have one reliable source of truth.

APIs and CRM integrations

Integrations are designed as part of the architecture, not as an add-on before launch. For each exchange, you need to determine the owner of the data, the format, the frequency of synchronization, error handling, and the rules for resending. Otherwise, CRM quickly becomes another database that employees check with 1C and the site manually.

Identify the owner of the data first

Before selecting a rest, webhook, or queue, create a data map. For each object, it should be clear which system creates the record and which has the right to change the critical fields.

Data Possible source of truth What to sync
Lead & Referral CRM source, contacts, consents, responsible, processing status
Nomenclature and residues 1C or ERP SKU, price, warehouse, availability, tax features
Website Order online store or CRM composition, delivery, payment, external ID, customer
Invoice & Execution 1C or ERP number, amount, payment status and closing documents
Buzzer telephony number, record, duration, result and connection with the transaction
Promotional Expenses advertising system or analytics repository campaign, costs, labels and period

If two systems are considered owners of the same field at the same time, a conflict is inevitable. For example, the manager changes the phone number in the CRM, the client in the personal account, and the nightly import from 1C returns the old value. The priority rule and change log shall be part of the terms of reference.

Rest API, webhook or queue

These mechanisms solve different tasks and are often used together.

Arrangements When it fits Restriction
REST API the user expects an immediate response: finding a customer, calculating the price, receiving a card external service may slow down or disrupt the user's request
Webhook need to report the event without constant questioning delivery may repeat, come out of order or temporarily fail
Message queue processing takes time, needs re-delivery or insulation of external system monitoring, retry policy and handling of raw messages required
Periodic reconciliation the external system does not support events or you need to confirm the completeness of the data changes appear with a delay, the load on the API increases

The synchronous API should not be used where the manager or client may not wait for the operation to complete. For example, creating a request should quickly return confirmation, and it is better to continue sending data to several external systems in the background.

Webhook also does not guarantee exactly one delivery. The correct recipient must withstand repetition, delay, and reordering of events.

How to protect yourself from duplicates

To protect against duplicates, processors make idempotent: the redelivery of one event should not create a second transaction, re-charge the payment or double change the balance.

The minimum event contract includes:

{
  "eventId": "order-18452-paid-v1",
  "eventType": "order.paid",
  "occurredAt": "2026-08-26T08:30:00Z",
  "source": "online-store",
  "entityId": "18452",
  "schemaVersion": 1
}

The recipient saves eventId or the idempotent key along with the result of the operation. If the event comes again, the system returns the same result and does not perform the business action a second time.

Phone searches alone are not enough. The number can change, be used by several people, or get into the database in different formats. To exchange, you need stable external identifiers and a table of correspondences between system IDs.

Retry, backoff and error queue

You only need to repeat temporary errors: timeout, unavailability of the service, 429 or part of the answers 5xx. Validation error 400 it makes no sense to send again without changing the data.

Practical scheme:

  1. Record the event and business operation in your database.
  2. Submit a task to the queue after a successful transaction.
  3. If a temporary error occurs, try again with an increasing delay.
  4. Limit the number of attempts.
  5. Move the unprocessed message to the dead-letter queue.
  6. Notify the responsible person and keep an understandable reason for the failure.
  7. After the fix, it is safe to replay the event.

The confirmation of the queue message should occur after successful processing, and not immediately after receipt. At the same time, the system is still obliged to withstand a repeat: the connection may be terminated after recording the result, but before sending the confirmation to the broker.

Versioning of contracts

A documented API is not useful for a beautiful Swagger page. The specification captures field names, types, binding, error codes, and authorization schemes. This allows you to check the compatibility of the CRM, site and integration service before deployment.

When changing the contract:

  • do not change the meaning of the existing field without a new version;
  • add new optional fields so that the old recipient continues to work.
  • determine in advance the period of support for the old version;
  • Store schemaVersion in asynchronous events;
  • check the contract in CI if the integration is critical for sales or accounting.

Exchange Log & Reconciliation

Integration should not be a black box. For each operation, store:

  • a correlation ID;
  • source and recipient;
  • event type and entity ID;
  • the time of the first and last attempt;
  • code and short response of the external system;
  • Ends after
  • final status;
  • a link to record or trace without keeping secrets.

Metrics and distributed tracing help to see the growth of errors, but do not replace business reconciliation. For example, once a day you can compare the number of paid orders, amounts and missing external IDs between CRM and 1C. Such a check finds quiet discrepancies that are not accompanied by 500.

Integrations with:

  • 1C and ERP;
  • website and application forms;
  • IP telephony;
  • mail and instant messengers;
  • payment services;
  • advertising offices and end-to-end analytics.

If there are several exchanges, it is useful to allocate an integration layer. But a separate bus or microservice is not needed automatically: for a small project, an isolated module with a queue, a log and understandable contracts is enough.

How to Accept CRM Integration Before Launch

Acceptance should check not only the successful request, but also the failures. Minimum set of scenarios:

  1. A new record is transmitted once and receives an external ID.
  2. Repeating the same event does not create a duplicate.
  3. Temporary 500 results in retry rather than data loss.
  4. A validation error falls to the responsible person with an understandable reason.
  5. Events that occur out of order do not return the entity to its old status.
  6. The change of the contact is synchronized according to the agreed rule of the data owner.
  7. Unavailability of 1C, telephony, or CRM does not block the submission of an application on the website.
  8. Secrets and personal data are not displayed in open logs.
  9. After recovery, you can safely replay the error queue.
  10. Daily reconciliation finds missing documents and discrepancies in amounts.

To integrate with 1C, time zones, rounding, VAT rates, partial payments, refunds, order cancellation and composition changes are checked separately after the document is created. It is the boundary scenarios that often distinguish the working exchange from the demonstration prototype.

Frontend CRM: working tool interface

A manager can spend the entire working day in CRM, so the frontend affects productivity no less than the backend. React, Next.js, Vue and Nuxt allow you to build dynamic interfaces, but choosing a library alone does not solve UX problems.

It is more important to ensure:

  • quick search and opening of a card;
  • saving drafts and protecting against data loss;
  • virtualization of long tables;
  • clear loading states and errors;
  • keyboard navigation;
  • adaptation of critical scenarios for tablets and phones;
  • updates via WebSocket where real-time is really needed.

For example, in the case SPA Booking & CRM System synchronization via Node.js and sockets was used to instantly update reservations with the front desk staff. This is a specific business problem, not technology for technology's sake.

CRM Security

CRM contains personal data, communication history and commercial information. The basic safety loop includes:

  • role model and the principle of minimal authority;
  • MFA for employees with expanded access;
  • encryption of connections and secrets;
  • logging actions with sensitive data;
  • backup and regular recovery checks;
  • restriction and rotation of external integration keys;
  • separate production, staging and development environments;
  • control of unloadings and mass operations.

The requirements of Federal Law 152, GDPR and internal policies must be taken into account before choosing a location and backup scheme.

How to Choose a CRM Technology Stack

A practical algorithm looks like this:

  1. Describe roles, processes, entities, and integrations.
  2. Determine non-functional requirements: load, availability, security and recovery time.
  3. Select a minimal architecture that meets these requirements.
  4. Check if the current command can support the selected stack.
  5. Fix module boundaries and API contracts.
  6. Assign the data owner and external identifiers for each integration.
  7. Create a prototype of the most risky exchanges and check for failures.
  8. Launch the first useful circuit and test it on real users.

For most CRMs, a reasonable starting point is a modular monolith, PostgreSQL, a documented API, a background task queue, and separate services only for truly independent or high-load functions.

FAQ

Is it necessary to build a CRM on microservices?

No. Microservices are needed when there is a proven need to scale, deploy, or isolate independently. For MVP and mid-scale systems, a modular monolith is usually simpler and more reliable.

Is Node.js suitable for backend CRM?

Yes, especially for APIs, WebSockets and a lot of integrations. But the choice must be correlated with the team’s competencies, data requirements and existing infrastructure.

Which database to choose for CRM?

PostgreSQL covers most of the transactional tasks of CRM. Search, analytics, and semantic processing can use additional specialized storage.

How to integrate CRM with 1C without duplicates?

You need to assign a source of truth for orders, customers, items and payment, use stable external IDs and idempotent events. Re-delivery should not re-create the document, and exchange errors should fall into the controlled queue.

Do I need a message queue to integrate CRM?

Not always. It is needed if the operation takes a long time, the external service may be unavailable or the event cannot be lost. An API is enough for simple synchronous data reading, but background exchanges with 1C, telephony and mailings are usually more reliable through the task queue.

Why does webhook create duplicates in CRM?

The provider can retry the webhook if it has not received confirmation or a network failure has occurred. The handler must check eventId or an idempotent key and return the result of the operation already performed without creating a second record.

How to estimate the period of CRM integration development?

The term does not depend on the number of endpoints, but on the quality of documentation, data model, authorization, limits, error handling and the test loop of the external system. Before the assessment, it is useful to collect examples of objects, a field map, and a list of boundary scenarios.

When should architecture be revised?

When measurable limitations have appeared: response time is increasing, releases of one module are blocking the others, a separate function requires independent scaling, or the current data model interferes with the development of the product.

Bottom line

A good custom CRM architecture starts with processes and requirements, not a list of trendy technologies. Backend, frontend, database, and integrations should form a system that the team can safely develop after the first release. The reliability of the exchange is determined not by the fact of API connection, but by the behavior in case of repetition, delay, partial failure and data discrepancy.

To compare technical solutions with budget, study the material what does the cost of CRM consist of?. To prepare requirements, use CRM design guide, and for project evaluation - page custom CRM system development.

Sources

Leave your contacts - we will call you back, sort out the problem and offer the best way. We have more than 350 projects behind us, each of which we launched with an individual approach. We guarantee expert advice during business hours.