Enterprise briefs describe the visible thing. A portal for dealers, an app for field engineers, a reordering screen for buyers. The visible thing is almost never the work. The work is that the price lives in the ERP, the customer lives in the CRM, the stock figure is true for four minutes at a time, and none of those three systems will be modified, replaced or even restarted to accommodate what you are building. Scope the project as the screens and it will be late. Scope it as the integration and it will be roughly right.
The first meeting should settle one question per entity: which system is the record of truth. For a customer, a price, a stock level, an order, an invoice — exactly one system owns it, everything else holds a copy, and copies are allowed to be stale but never authoritative. Most of the disagreements that surface in month four are two systems both believing they own the same field, with two teams having quietly built around their own belief.
Then the unglamorous discovery of how the data actually arrives. Enterprise systems rarely offer the clean REST API the proposal assumed. What is on offer is a SOAP endpoint documented in 2011, a nightly CSV dropped on an SFTP server, a read-only database view behind a VPN, a message queue nobody has restarted in years, or a vendor who will happily build an API for a fee and a nine-month lead time. This is not an obstacle to be complained about. It is the input to the design, and it needs to be known in week one rather than week nine.
What protects the new system is a translation layer, and the discipline to keep it thin and total. The ERP's model does not enter the application. Its field names, its codes, its idea of what a customer is, its habit of returning an empty string for a missing number — all of it is converted at the boundary into a schema written for the product, held in PostgreSQL, owned by the new system. Skip that layer and the integration stops being a component and becomes the architecture: five years later nobody can change a screen without understanding an ERP module.
Synchronisation is then a choice between two honest patterns and one dishonest one. Batch, where a schedule pulls and reconciles, which is simple, predictable and always a little behind. Event-driven, where the source publishes changes and the new system reacts, which is current and requires the source to be capable of it. The dishonest pattern is calling the ERP live on every page load, which appears to be real-time until the ERP has a slow afternoon and takes your product down with it. Whichever is chosen, three things are not optional: idempotent writes, because the same message will arrive twice; a retry policy with a dead letter queue, because it will fail; and a reconciliation report that a human reads, because silent drift is the failure mode nobody detects for months.
Design for the source being unavailable, because it will be. The right behaviour is almost never an error screen. An order taken while the ERP is down should queue, be visibly marked as pending, and settle when the connection returns. A price that cannot be refreshed should be shown with its age rather than hidden. Degrading in a way the user understands is a product decision made in advance, and it is what separates a system people keep using during an outage from one they stop trusting afterwards.
In Greece there is a further layer that is not optional and is usually underestimated: myDATA transmission and invoicing rules, courier integrations with their own formats and failure modes, bank and payment provider requirements, and marketplace feeds. On rented platforms each of these is a plugin with a subscription and an owner who may lose interest. In a custom system they are code you own, which is more work at the start and the reason the operational cost stops growing at year three.
The last thing is not technical at all, and it is the thing that slips schedules. Integration work depends on other people's teams: credentials, a test environment that resembles production, rate limits raised, a VPN account, an approval from a system owner who has nine other priorities. Put those on the plan as named dependencies with dates and owners on the client side. The slowest part of an integration project is almost never the code.
Common questions
- Why do ERP integrations take longer than expected?
- Because the delay is rarely in the code. It is in obtaining credentials and a usable test environment, in discovering that the available interface is a nightly file or an old SOAP service rather than an API, and in resolving which system owns each field. Those are organisational dependencies with other owners, and they should appear on the plan with names and dates rather than being assumed.
- Should an app call the ERP directly?
- No. Calls should pass through a translation layer that converts the ERP's model into a schema owned by the new system and stored in its own database. This keeps the product usable when the source is slow or down, prevents the legacy data model from spreading through the application, and means the ERP can eventually be replaced without rewriting the product.
- Batch synchronisation or event-driven?
- Batch when the source cannot publish events and a few minutes of staleness is acceptable — it is simpler and more predictable. Event-driven when the source supports it and the business genuinely needs current data, such as stock during a sale. Both require idempotent writes, a retry policy with a dead letter queue, and a reconciliation report a person actually reads.
- What should happen when the ERP is unavailable?
- The product should degrade visibly rather than fail. Orders queue and are marked pending, prices are shown with their age, and the system settles automatically when the connection returns. That behaviour is specified during design, not improvised during the first outage.
The service behind it
Custom Web Development
Custom web development in Athens: singular, extremely fast websites written from zero in React and Next.js for the brand they belong to. No templates.