Thesis: People and deals belong in the CRM. Weekly counters stay on the event store that recorded them. After a cutover, match the ACL to the CRM objects — do not expect the CRM to invent analytics it never stored.
What we shipped first
We moved contacts and opportunities into the product CRM. The weekly ops board still queried the CRM for “briefs this week.” The pipeline column went blank. The counter API on api.example.com still had the numbers. The board had pointed at the wrong persistence boundary.
The working shape
Keep people and deals on crm.example.com. Keep weekly counters on the event store. When the CRM becomes the source of truth for pipeline, grant the board ACL to those objects — and leave analytics where the events already landed.
// Persistence boundary: people/deals in CRM; weekly counters stay on the event store.
const crm = {
host: "crm.example.com",
people: [{ id: "p_1", name: "Alex", companyId: "c_9" }],
opportunities: [{ id: "o_1", stage: "brief", personId: "p_1" }],
};
const eventStore = {
host: "api.example.com",
counters: { briefs_this_week: 4, fit_notes_sent: 2 },
};
// Board after CRM cutover — ACL must allow the CRM objects.
// Counters still read from the event store, not from CRM stages.
function boardSnapshot({ crmAclOk }) {
return {
pipeline: crmAclOk ? crm.opportunities : [],
week: eventStore.counters,
};
}
boardSnapshot({ crmAclOk: false }); // { pipeline: [], week: { … } } — empty board
boardSnapshot({ crmAclOk: true }); // people/deals visible; counters unchanged
Checklist
- Name which system owns people/deals vs which owns counters.
- After a CRM cutover, verify the board ACL against CRM objects — not only the old event store.
- Do not mirror weekly counters into CRM stages just to fill a board.
Related: Reading Mode dropped the FAQ answers. How we deliver: methodology · readiness.
Engineering commentary only — not audit, legal, or certification advice.