Save a contact and its affiliations
What It Does
This workflow commits a correction to the person and any affected contact-owned relationships as one aggregate. A coordinator can replace Ruth’s work number and record an employer change in a single reviewed save, without leaving the title, primary organization and responsibilities describing different intended results.
Our reading is that the original read version is essential because the same aggregate also changes through spouse maintenance, source disconnection and scheduled work. The console reviews bound differences and submits a patch; the service also accepts replacements, whose observed omission behavior remains a separate implementation gap against the approved complete-payload requirement.
What Starts It
An editor submits the contact draft or an integration supplies replacement or patch input. Organization roster actions also use the owning contact write. The saved base version identifies what the editor read before changing details or relationships. The workflow begins validation at submission; local chooser and collection edits alone leave the saved aggregate unchanged.
The Flow
The diagram shows the sequence and service seams. Each synchronous stage waits for its result, while captured-event, queue and scheduled stages can run after the originating request has returned. The hop descriptions below identify the write and failure boundary, so the last arrow must not be mistaken for one atomic transaction across every participant.
Hop by Hop
1. Console contact editor
The contact editor compares its draft with the original record and presents Review Changes. Its JSON Patch begins with the original version test and includes edited bindings. Choosing to continue after a conflict keeps that original basis; it does not silently rebase the draft onto a newer contact.
2. Entities route
The route evaluates update authority and the relevant unlock capability, applies the patch to the read record or maps a replacement, and validates the result. Missing version tests, absent or merged identities and unauthorized lock changes are synchronous refusals. No successful route gate bypasses persistence guards.
3. Shared record-asset pipeline
Changed photo and file custom values are prepared through the asset pipeline before the save transaction. A failure leaves the previously stored contact reference in place. Newly prepared copies from a later failed save can remain outside the transaction until cleanup discovers them.
4. Entities persistence (one transaction)
Persistence locks the contact, compares the version and assembles relationship work before writing. Contact points, spouse references, affiliations, responsibilities, defaults and custom values share the transaction. A net unchanged save restores modification stamps; a stale request conflicts before that no-op decision.
5. Database change capture
Database capture records one aggregate explanation of changed contact data. A reciprocal spouse change has its own subject capture. Attribution failure rolls back the business change along with its history instead of allowing a correction with missing actor or source context.
6. Entities route
After the aggregate commits, the route asks dispatch to run promptly. Failure of this nudge is not a reason to undo the saved contact. Scheduled dispatch can publish the captured change later, and dependent projections remain temporarily behind that committed result.
7. CRM change-event consumers
Change consumers reevaluate organizations affected by affiliation or role differences and separately process region, search and duplicate work. Queue retries apply to the failing handler message. The legacy save escape and import suppression remain part of observed compliance behavior and are not new recommended editing tools.
When Something Fails
Hop 1 — Console contact editor
The failure boundary is: Offer reload or comparison after conflict; continuing to edit retains the original base version. The remaining work or data is: No partial business write remains. The editor presents conflict recovery.
Hop 2 — Entities route
The failure boundary is: Refuse missing version tests, forbidden lock changes, absent records or merged identities. The remaining work or data is: No partial business write remains. The caller receives the result.
Hop 3 — Shared record-asset pipeline
The failure boundary is: Refuse asset preparation while retaining the previously saved file. The remaining work or data is: Copies prepared for a later failed save await cleanup. Asset cleanup records identify abandoned copies.
Hop 4 — Entities persistence (one transaction)
The failure boundary is: Roll back every transactional change; stale-version errors identify the latest modifier. The remaining work or data is: Only prepared asset copies may remain outside the rollback. The caller receives the result.
Hop 5 — Database change capture
The failure boundary is: Refuse capture without required attribution and roll back the save. The remaining work or data is: No partial business write remains. The saved contact revision is available in history.
Hop 6 — Entities route
The failure boundary is: A failed nudge is ignored; scheduled dispatch remains available. The remaining work or data is: Dependent reactions wait for later publication. Dispatcher logs identify delayed publication. A later attempt can be required; the scheduling or publication boundary does not establish an immediate user-visible completion.
Hop 7 — CRM change-event consumers
The failure boundary is: Retry the failed handler through SQS and dead-letter after five receives. The remaining work or data is: Saved contact data can precede updated compliance, region and search state. Handler logs and dead-letter messages expose the lag. This failure is downstream of committed input; operators investigate its own delivery rather than treating the caller’s original save as failed.
Repeats and Races
The service checks version before deciding that content is unchanged. A current-version no-op preserves stamps, revision and event count, but the same proposed content based on an older version still conflicts. After an uncertain response, a caller reads the contact and compares the committed result before constructing another mutation.
Contact locking serializes editors with pending-role settlement. Relevant organizations are also locked for assignment decisions before shared-detail work. These locks coordinate known races but do not establish universal deadlock freedom across records. The refresh-recovery findings still limit what has been proven through the browser.
What Is Observable
The editor presents conflict recovery. The caller receives the result. Asset cleanup records identify abandoned copies. The saved contact revision is available in history. Dispatcher logs identify delayed publication. Handler logs and dead-letter messages expose the lag.
The person or initiating caller sees the synchronous result at its own boundary. Captured history establishes committed local changes; logs, durable job state and queue outcomes explain remaining work. A successful database change, accepted downstream request and completed delivery are different observations and must be investigated through the component that owns each one.
Open Design Questions
The current replacement path clears some omitted fields while preserving omitted affiliations and applying the custom-field contract separately. The approved complete-payload requirement remains unresolved in implementation. Ending and removal through a general contact save also retain their existing disagreement with Platform’s explicit-operation requirement.