Design system / rev A

Event Storming Board

A modelling surface for change-management requests. The chrome is monochrome steel on paper; all color in the product belongs to the sticky grammar, so the timeline reads as a legend at a glance. Conventions follow eventstorming.com, adapted where the digital medium can do better than a paper wall.

base
Industry
ground
#f2f2f3
steel
#5980a6
grid
8px / lane 208

CML — the sixth tab

A lossy export today. The parking tray is the honest part.

Context Mapper’s CML is a validated text model with a real generator pipeline behind it, and it is the closest thing to a source of truth this board has. It is also missing roughly half of what an event storming board holds: policies, read models, hotspots, phases, geometry, threads, the glossary. So the tab follows the §16 rule — generate, don’t duplicate, park what doesn’t map — and the tray is deliberately the loudest thing on the screen.

Valid CML. Half the board is missing from it, by design.

ContextMap VendorOnboardingChange {
  type = SYSTEM_LANDSCAPE
  state = AS_IS
  contains IntakeAndTriage, RiskAndCompliance, Finance, Scheduling, VendorMasterData, ChangeExecution, ServiceNow
  Scheduling -> ChangeExecution {
  }
  ChangeExecution -> IntakeAndTriage {
  }
  RiskAndCompliance [U,S]->[D,C] Finance {
    implementationTechnology = "REST · POST /budget-reservations"
    downstreamRights = INFLUENCER
    exposedAggregates = RiskAssessment
  }
  Finance -> RiskAndCompliance {
  }
  RiskAndCompliance [U,OHS,PL]->[D] Scheduling {
    implementationTechnology = "Kafka topic · change.approved.v2"
    downstreamRights = VETO_RIGHT
    exposedAggregates = ChangeRequest, RiskRating
  }
  Scheduling -> VendorMasterData {
  }
  VendorMasterData -> ChangeExecution {
  }
  ChangeExecution -> IntakeAndTriage {
  }
  ChangeExecution -> Scheduling {
  }
  IntakeAndTriage -> ChangeExecution {
  }
  ServiceNow -> IntakeAndTriage {
  }
  RiskAndCompliance -> Finance {
  }
}

Domain VendorOnboardingChangeDomain {
  Subdomain IntakeAndTriageSubdomain {
    type = SUPPORTING_DOMAIN
  }
  Subdomain RiskAndComplianceSubdomain {
    type = CORE_DOMAIN
  }
  Subdomain FinanceSubdomain {
    type = SUPPORTING_DOMAIN
  }
  Subdomain SchedulingSubdomain {
    type = GENERIC_SUBDOMAIN
  }
  Subdomain VendorMasterDataSubdomain {
    type = GENERIC_SUBDOMAIN
  }
  Subdomain ChangeExecutionSubdomain {
    type = SUPPORTING_DOMAIN
  }
}

BoundedContext IntakeAndTriage implements IntakeAndTriageSubdomain {
  domainVisionStatement = "Take a change request in through either channel and decide whether it is worth assessing."
  businessModel = "costReduction"
  evolution = PRODUCT_RENTAL
  responsibilities = "gatewayContext"
  Aggregate ChangeRequest {
    Entity ChangeRequest {
      aggregateRoot
    }
    Command SubmitChangeRequest
    Command TriageRequest
    Command CloseChangeRequest
    DomainEvent ChangeRequestSubmitted
    DomainEvent RequestAcceptedForAssessment
    DomainEvent ChangeRequestClosed
  }
}

Stakeholders of IntakeAndTriage {
  Stakeholder RequestingManager {
    influence = MEDIUM
    interest = HIGH
  }
  Stakeholder ServiceDesk {
    influence = LOW
    interest = MEDIUM
  }
}

BoundedContext RiskAndCompliance implements RiskAndComplianceSubdomain {
  domainVisionStatement = "Decide whether a proposed change may proceed, and record why. Owns risk rating, security review and CAB routing for every change request; owns nothing about scheduling or vendor data."
  businessModel = "compliance"
  evolution = CUSTOM_BUILT
  responsibilities = "analysisContext"
  Aggregate RiskAssessment {
    Entity RiskAssessment {
      aggregateRoot
    }
    Command AssessVendorRisk
    Command RequestSecurityReview
    Command ApproveChange
    DomainEvent RiskRatingAssigned
    DomainEvent SecurityReviewPassed
    DomainEvent ChangeApprovedByCAB
  }
  Application {
    Event EmergencyChangeRoutedAsAStandardChange
  }
}

Stakeholders of RiskAndCompliance {
  Stakeholder RiskAnalyst {
    influence = MEDIUM
    interest = HIGH
  }
  Stakeholder CABChair {
    influence = HIGH
    interest = HIGH
  }
}

BoundedContext Finance implements FinanceSubdomain {
  domainVisionStatement = "Reserve and release the money a change needs."
  businessModel = "costReduction"
  evolution = COMMODITY
  responsibilities = "executionContext"
  Application {
    Command ReserveBudget
    Command ReRunCreditCheck
    Event BudgetReserved
    Event FinanceReChecksTheBudget
  }
}

Stakeholders of Finance {
  Stakeholder FinancePartner {
    influence = MEDIUM
    interest = MEDIUM
  }
}

BoundedContext Scheduling implements SchedulingSubdomain {
  domainVisionStatement = "Find a window that collides with nothing else."
  evolution = COMMODITY
  responsibilities = "executionContext"
  Aggregate ChangeWindow {
    Entity ChangeWindow {
      aggregateRoot
    }
    Command ScheduleChangeWindow
    Command ImplementChange
    DomainEvent WindowScheduled
    DomainEvent ChangeImplementedInProduction
  }
}

Stakeholders of Scheduling {
  Stakeholder ChangeManager {
    influence = HIGH
    interest = HIGH
  }
}

BoundedContext VendorMasterData implements VendorMasterDataSubdomain {
  domainVisionStatement = "Hold the one vendor record everything else refers to."
  evolution = PRODUCT_RENTAL
  responsibilities = "executionContext"
  Aggregate Vendor {
    Entity Vendor {
      aggregateRoot
    }
    Command CreateVendorRecord
    Command ActivateVendor
    DomainEvent VendorRecordCreated
    DomainEvent VendorActivatedInMasterData
  }
}

Stakeholders of VendorMasterData {
  Stakeholder MDMSteward {
    influence = LOW
    interest = HIGH
  }
}

BoundedContext ChangeExecution implements ChangeExecutionSubdomain {
  domainVisionStatement = "Carry the change out and prove that it landed."
  evolution = CUSTOM_BUILT
  responsibilities = "executionContext"
}

Stakeholders of ChangeExecution {
  Stakeholder PlatformEngineer {
  }
}

BoundedContext ServiceNow {
}

Parking tray

38 OBJECTS WILL NOT SURVIVE

POLICIES
4
READ MODELS
6
HOTSPOTS / QUESTIONS
6
ASSUMED STICKIES
3
UNKNOWN SPANS
2
PHASES · TIMEBOXES
4
GLOSSARY TERMS
4
THREADS · FACILITATOR
2
ALL GEOMETRY
—
NAME COLLIDES IN CML
0
NOT ITEMISED
7

The tray is not a warning to dismiss. It is the list of things a reader of the CML file will not know, and it prints with the export.

Fidelity: the parking tray’s counts differ from the source’s printed figures — it prints 6 policies / 3 read models against §10’s 4 and 6, and itemises 26 against a printed total of 31. `parkingTray()` computes honestly from the model, which is INVENTORY §9 amendment 12.The tray now carries a tenth row, NAME COLLIDES IN CML, that the source’s figure does not print: it is what the board actually holds, and hiding it would be the one thing §24 says the tray must never do.
Rules
  • Export, not round trip The tab is one-way and read-only. Nothing typed in CML comes back, so there is still one source of truth and §16’s promise holds.
  • What it buys A diffable, git-native artifact for the change pack, plus everything downstream of CML: PlantUML, MDSL service contracts, the generated map.
  • Open — the store The intent is that CML becomes where the board lives between sessions. That cannot happen on plain CML; it needs the extended dialect above, owned and versioned by us. Decide that before building the L1 change pack on top of it.