ShaDey Inc.Domain-Driven DesignMaster Data ManagementMulti-Region
DDD, MDM & Multi-Region Modeling
Bounded contexts for eight enterprise domains, golden record survivorship rules, and global-core / regional-extension data architecture for ShaDey Inc.'s expansion into North America, EMEA, and Asia-Pacific.
1Why DDD for Data Modeling?
Domain-Driven Design is a methodology for handling complexity in large software systems by decomposing them into bounded contexts. When applied to enterprise data architecture, DDD helps isolate data semantics, define explicit ownership, and prevent semantic collisions across organizational domains.
ShaDey Inc. operates eight distinct business domains, each with their own vocabulary, rules, and understanding of shared entities. The term "Customer" has profoundly different meanings:
Without explicit bounded contexts, teams build conflicting data models for the same entity. DDD forces you to name the conflict: "Sales defines a Customer as an account with an active opportunity; Finance defines it as a legal entity with credit exposure." These are different concepts and deserve different tables.
The payoff: By decomposing into bounded contexts first, the data model emerges naturally. Each domain gets a clear definition of which entities it owns, which it consumes, and which it shares. Data governance becomes mechanical: ownership is explicit, not assumed.
2Bounded Contexts: Drawing the Lines
A bounded context is an explicit boundary within which a domain model is valid. Inside the boundary, terms are unambiguous. Outside the boundary, a formal translation happens (Anti-Corruption Layer). ShaDey Inc.'s eight bounded contexts map cleanly onto the organization chart.
Bounded Context Map: Eight distinct business domains (Sales, Marketing, Supply Chain, Finance, Loyalty, eCommerce, Store Operations, Customer Service) with shared entities (Customer, Product, Order) appearing across multiple contexts. Shared Kernel and Anti-Corruption Layer patterns show context interaction points.
Context Mapping Patterns
Shared Kernel: Sales and Marketing share Customer (account name, industry) but with different extensions (Sales adds sales_rep_id, Marketing adds segment_id). Customer-Supplier: Sales provides SalesOrder → Finance consumes Billing. Finance is downstream; Sales owns the contract. Anti-Corruption Layer: eCommerce provides Shopper; Sales consumes via ACL that maps Shopper.email → Customer.contact_email.
Entity Name
Sales Context
Marketing Context
Finance Context
Customer
Account with sales org, dist. channel, division
Campaign member with engagement metrics
Bill-to legal entity with credit terms
Product
Sellable item with discount matrix
Promotable bundle with shelf planogram
GL-linked item with cost allocation
Order
Sales order (VBAK) with line items
Campaign response event
Invoice (VBRK) with payment terms
3Shared vs Domain-Local Entities
Some entities are truly shared (Customer, Product, Date); most are domain-local (Campaign, Store Layout, SalesRep). Ownership rules determine which domain is the system of record and which are consumers.
Entity
Sales
Marketing
Supply Chain
Finance
Owner
Customer
Owner
Consumer
Consumer
Consumer
Sales
Product
Consumer
Consumer
Owner
Consumer
Supply Chain
Date
Shared
Shared
Shared
Shared
Platform
SalesOrder
Owner
Not Used
Consumer
Consumer
Sales
Campaign
Not Used
Owner
Not Used
Not Used
Marketing
Promotion
Consumer
Owner
Not Used
Consumer
Marketing
Inventory
Consumer
Not Used
Owner
Not Used
Supply Chain
LoyaltyMember
Consumer
Consumer
Not Used
Not Used
Loyalty
Ownership Rules
Owner: System of record, authoritative for all attributes, owns the SCD (slowly-changing dimension) history. Consumer: Reads the entity, may have local extensions (prefixed with domain initials: mkt_segment_id), never modifies owner's attributes. Not Used: Domain has no dependency on this entity.
Example: Product is owned by Supply Chain (MARA in SAP). Sales consumes it and adds product_discount_matrix_id. Finance consumes it and adds cost_center_allocation. Marketing consumes it and adds promotion_id and shelf_planogram_location. None of these local extensions are visible to other domains; they live in domain-specific extension tables.
4Anti-Corruption Layer Pattern
When one bounded context depends on another but cannot fully control it, an Anti-Corruption Layer (ACL) sits between them. The ACL translates and isolates domain models, protecting downstream from upstream changes.
Concrete example: ShaDey Inc. is upgrading SAP from ECC to S/4HANA. The Finance domain (downstream) needs LedgerPosting. The SAP system (upstream) is changing: ECC uses BKPF (header) + BSEG (detail); S/4HANA splits accounting into ACDOCA (universal journal). Without an ACL, Finance's entire model breaks.
With an ACL: Finance defines dim_ledger_posting in their language. The ACL exposes two implementations: one for ECC (joins BKPF + BSEG), one for S/4HANA (reads ACDOCA); and unifies the output. Finance's model never changes.
Finance then consumes a unified view. A dim_ledger_posting table uses a UNION or parameterized logic to pick the correct ACL implementation based on the posting period. The Finance domain model never changes; the translation layer absorbs upstream complexity.
ACL Benefits
1. Downstream contexts (Finance) remain stable during upstream migrations (ECC → S/4HANA).
2. Translation logic isolated in one place, not scattered in 50 ETL pipelines.
3. Easy to support dual-run scenarios: ECC + S/4 running in parallel, one logical view.
5Master Data Management: Golden Record Design
Master data is the single source of truth for business entities. A golden record is not just the "survivor" from multiple sources: it's a deliberate design with survivorship intent: each attribute comes from the source system most authoritative for that attribute.
Customer Golden Record: Survivorship Intent (ShaDey Inc.)
Attribute
Owner (System of Record)
Confidence Score
Last Verified Logic
legal_name
SAP FI (KNB1)
95%
Audited for SOX compliance; last verified by Finance
email
eCommerce (self-service)
88%
Customer can update; validated by opt-in confirmation
phone
CRM (Salesforce)
82%
Sales rep updates; last verified during account review
shipping_address
eCommerce
91%
Customer-entered; validated by successful delivery
billing_address
SAP (KNA1)
97%
Verified by invoice delivery; verified by Finance
segment_code
Marketing (CRM segmentation)
75%
Recalculated monthly; last verified by Marketing Manager
credit_limit
Finance (SAP FI)
94%
Approved by credit committee; last updated 6 months ago
industry_code
CRM (manual classification)
68%
Sales rep enters; not validated; needs review
Survivorship Intent vs Conflict Resolution
Intent: "The marketing segmentation system is the authoritative source for segment_code because the Marketing team owns the segmentation logic." Never: "Pick the most recent value." That's conflict resolution, not intent. Intent is declarative: this system is the source of truth for this attribute.
Each attribute gets a confidence score: how often the source is actually right. legal_name from SAP is 95% confident (audited for SOX); industry_code from CRM is 68% (sales reps guess). Low confidence signals: data quality issue, need for cleansing, or wrong owner.
The golden record is not a table of survivors. It's a specification. Each consumer context (Sales, Marketing, Finance) queries it with a SELECT that picks the surviving attribute. The confidence score tells them: "This email is 88% likely correct, and hasn't been verified in 45 days."
6Conflict Detection Modeling
Conflict detection is not conflict resolution. You detect misalignment, surface it to humans, and let them decide. Modeling conflicts means building explicit queries and workflow tables.
Example: SAP says customer address is "123 Main St, Chicago, IL 60601". CRM says "123 Main Street, Chicago, IL 60601" (spelling variation). eCommerce says "123 Main St, Chicago, IL 60614" (different ZIP). These are conflicts. The golden record flow must:
1. Detect that three sources disagree on shipping_address for customer_id = 98765
2. Log the conflict to mdm_conflict_log with all three versions and source systems
3. Flag the golden record as conflict_status = 'UNRESOLVED' and requires_manual_review = TRUE
4. Notify the data steward via workflow (email or queue)
5. Steward decides which source is authoritative (eCommerce is customer self-serve, trust it) and marks resolution
6. Golden record updates: pick the winning source, reset conflict_status = 'RESOLVED'
Spelling variance: SAP "Main St" vs CRM "Main Street": auto-resolvable via fuzzy matching. Genuine mismatch: Different ZIPs: requires human decision (which address is current?). Timeliness lag: Old value in System A vs new value in System B: resolve based on timestamp.
7Multi-Region Master Design: Global Core vs Regional Extensions
ShaDey Inc. is expanding globally: North America (NA), Europe/Middle East/Africa (EMEA), Asia-Pacific (APAC). The master data architecture must support global attributes (legal_name, customer_id) while allowing regional attributes (tax_ids, regulatory flags) and local overrides.
The pattern: Global core → regional extension → local override. Example: Product pricing.
dim_product_ext_na
• product_id
• na_base_price
• na_currency (USD)
• na_regulatory_class Source: NA Finance FK → Global
Local Override
dim_product_local_na_west
• product_id
• west_promotional_price
• west_shelf_location
• effective_from Source: NA West Store Ops FK → NA Extension
Attribute
Level
System of Record
Scope
Example Value
legal_name
Global
Supply Chain (MARA)
All regions
"Organic Cotton T-Shirt"
msrp
Global
Supply Chain (MARA)
All regions
USD 49.99 (reference only)
na_base_price
NA Regional
NA Finance
North America
USD 44.99
emea_base_price
EMEA Regional
EMEA Finance
Europe/Middle East/Africa
EUR 39.99
apac_base_price
APAC Regional
APAC Finance
Asia-Pacific
AUD 65.99
west_promotional_price
Local (NA-West)
NA West Store Ops
West US stores only
USD 34.99 (promotion)
gdpr_consent_date
EMEA Regional
EMEA Marketing
EMEA customers only
2024-03-15
-- Multi-Region Master Data: Global → Regional → LocalCREATE TABLEdim_customer_global (
customer_idBIGINTPRIMARY KEY,
global_customer_codeVARCHAR(50),
legal_nameVARCHAR(255),
primary_region_codeVARCHAR(10), -- 'NA', 'EMEA', 'APAC'country_codeVARCHAR(2), -- ISO 3166global_statusVARCHAR(50),
modified_tsTIMESTAMP
);
-- Regional Extension: NA (North America)CREATE TABLEdim_customer_ext_na (
customer_idBIGINTPRIMARY KEY,
na_customer_codeVARCHAR(50),
ssnVARCHAR(11), -- US Social Security (PII)state_tax_idVARCHAR(20),
na_credit_limitDECIMAL(12,2),
na_payment_termsVARCHAR(50),
FOREIGN KEY (customer_id) REFERENCESdim_customer_global
);
-- Regional Extension: EMEA (Europe/Middle East/Africa)CREATE TABLEdim_customer_ext_emea (
customer_idBIGINTPRIMARY KEY,
vat_numberVARCHAR(50), -- Tax IDgdpr_consent_dateDATE,
gdpr_consent_versionVARCHAR(20),
data_retention_daysINT, -- GDPR right to erasureemea_credit_limitDECIMAL(12,2),
FOREIGN KEY (customer_id) REFERENCESdim_customer_global
);
-- Regional Extension: APAC (Asia-Pacific)CREATE TABLEdim_customer_ext_apac (
customer_idBIGINTPRIMARY KEY,
abnVARCHAR(50), -- Australian Business Numbergst_registrationVARCHAR(50), -- Goods & Services Taxapac_credit_limitDECIMAL(12,2),
apac_payment_termsVARCHAR(50),
FOREIGN KEY (customer_id) REFERENCESdim_customer_global
);
Multi-Region Design Principles
Global first: Start with customer_id, legal_name, country. No regional data in global table. Regional extension: Tax IDs, regulatory flags, regional credit rules live here. FK integrity: Regional tables always have FK to global; never the reverse. Data sovereignty: EMEA customer data stays in EMEA databases (partition by region_code).
8Attribute Override Patterns
Regional extensions set defaults; local overrides temporarily change them. The key is precedence chains: when you query an attribute, you check Local → Regional → Global in order.
Example: Product pricing in ShaDey Inc. Global MSRP is USD 49.99. NA regional base is USD 44.99. West Coast promotional override is USD 34.99 for 90 days. The override chain resolves to: West override → NA base → Global MSRP.
Product ID
Global MSRP
NA Base Price
West Promo Price
Effective Price
Source
SKU-001
USD 49.99
USD 44.99
USD 34.99 (active until 2024-05-31)
USD 34.99
Local override
SKU-002
USD 29.99
USD 26.99
None (expired 2024-03-01)
USD 26.99
Regional base
SKU-003
EUR 39.99
N/A (EMEA)
N/A
EUR 39.99
Global MSRP
-- Override Resolution: Local → Regional → GlobalCREATE TABLEdim_product_price_finalASSELECTg.product_id,
g.sku,
g.name,
-- Override chain: Local > Regional > GlobalCOALESCE(
local.promotional_price,
regional.base_price,
g.msrp
) ASeffective_price,
-- Track which level provided the priceCASEWHENlocal.promotional_priceIS NOT NULL&&CURRENT_DATE() <=local.effective_untilTHEN'LOCAL_OVERRIDE'WHENregional.base_priceIS NOT NULLTHEN'REGIONAL_BASE'ELSE'GLOBAL_MSRP'ENDASprice_source,
local.effective_from,
local.effective_untilFROMdim_product_globalgLEFT JOINdim_product_ext_naregionalONg.product_id = regional.product_idLEFT JOINdim_product_local_na_westlocalONregional.product_id = local.product_id&&CURRENT_DATE() BETWEENlocal.effective_from&&local.effective_until;
Override Best Practices
1. Always include effective_from and effective_until dates. Overrides expire.
2. Never overwrite parent data; always use COALESCE or CASE to resolve chains.
3. Log every override: who created it, why, when it expires. Audit trail required.
4. Validate at each level: promotional price must be ≤ regional base; regional ≤ global MSRP.
9Regional Partition Strategies
Multi-region data must be partitioned for performance, compliance (GDPR: EMEA data stays in EMEA), and operational isolation. Partition key selection is critical.
Partition Strategy
Partition Key
Benefits
Challenges
By Region
region_code (NA, EMEA, APAC)
Data sovereignty; easy compliance; clean geographic isolation
Cross-region queries require multi-partition scan; global reporting slower
Recommended: Partition by Region with Range (Date)
-- Global customer table, partitioned by region + monthCREATE TABLEfact_customer_activity (
activity_idBIGINT,
customer_idBIGINT,
region_codeVARCHAR(10),
activity_dateDATE,
amountDECIMAL(12,2),
activity_typeVARCHAR(50)
)
PARTITION BY LIST (region_code) (
PARTITIONp_naVALUES IN ('NA')
SUBPARTITION BY RANGE (YEAR_MONTH(activity_date)) (
SUBPARTITIONp_na_202401VALUES LESS THAN ('202402'),
SUBPARTITIONp_na_202402VALUES LESS THAN ('202403')
),
PARTITIONp_emeaVALUES IN ('EMEA')
SUBPARTITION BY RANGE (YEAR_MONTH(activity_date)) (...),
PARTITIONp_apacVALUES IN ('APAC')
SUBPARTITION BY RANGE (YEAR_MONTH(activity_date)) (...)
);
Query Pattern
Execution Plan
Partition Pruning
WHERE region_code = 'NA' AND activity_date BETWEEN...
Scans p_na_202401, p_na_202402 only
Excellent (predicate filters both dimensions)
WHERE activity_date BETWEEN ... (no region)
Scans all regions' date partitions
Good (prunes by date, but must scan all regions)
SELECT COUNT(*) FROM fact_customer_activity
Full table scan
None
Partition Key Selection for ShaDey Inc.
Primary key:region_code (NA, EMEA, APAC): aligns with GDPR/data sovereignty. Subpartition:YEAR_MONTH(created_date): enables monthly archive/purge cycles. Index: Composite index on (region_code, created_date, customer_id) to support typical queries.
10Sync-Friendly Modeling
In a multi-region environment, regions must sync master data without creating conflicts. The model must support eventual consistency, detect conflicts, and avoid cross-region foreign keys.
Key principle: No cross-region foreign keys. Customer ID is global (created in NA HQ), but regional systems reference it via relaxed FK checks or eventual consistency guarantees. Never issue a query: "JOIN fact_sales_na TO fact_sales_emea on customer_id": regions are independent.
-- Sync-Friendly: Global Master with Regional Event Stream-- Master: created in NA, authoritative copyCREATE TABLEdim_customer_master_na (
customer_idBIGINT,
global_versionINT, -- Version counter for conflict detectionlegal_nameVARCHAR(255),
modified_tsTIMESTAMP,
modified_by_userVARCHAR(50),
PRIMARY KEY (customer_id)
);
-- Regional Copy: sync from NA, but supports local edits with conflict trackingCREATE TABLEdim_customer_replica_emea (
customer_idBIGINT,
global_versionINT, -- Tracks version from masterlocal_versionINT, -- Version of local editslegal_nameVARCHAR(255),
vat_numberVARCHAR(50),
modified_tsTIMESTAMP,
sync_statusVARCHAR(50), -- 'SYNCED', 'NEEDS_SYNC', 'CONFLICT'last_sync_tsTIMESTAMP,
PRIMARY KEY (customer_id)
);
-- Event log: all changes published to message queue (Kafka, Pub/Sub)CREATE TABLEmdm_sync_event_log (
event_idBIGINTPRIMARY KEY AUTO_INCREMENT,
entity_typeVARCHAR(50), -- 'CUSTOMER', 'PRODUCT'entity_idBIGINT,
event_typeVARCHAR(50), -- 'CREATED', 'UPDATED', 'DELETED'change_dataJSON,
source_regionVARCHAR(10), -- 'NA' (origin)timestampTIMESTAMP,
published_to_queueBOOLEAN
);
-- Sync logic: idempotent upsert with conflict detectionINSERT INTOdim_customer_replica_emea (
customer_id, global_version, legal_name, modified_ts, sync_status
)
SELECTna.customer_id,
na.global_version,
na.legal_name,
na.modified_ts,
'SYNCED'FROMdim_customer_master_nanaON CONFLICT (customer_id) DO UPDATE SETglobal_version = CASEWHENna.global_version=EXCLUDED.global_versionTHENna.global_version-- clean syncELSEna.global_version-- conflict: master winsEND,
sync_status = CASEWHENna.global_version>EXCLUDED.global_version&&EXCLUDED.local_version> 0
THEN'CONFLICT'-- master changed AND local edits existELSE'SYNCED'END,
last_sync_ts = CURRENT_TIMESTAMP();
Sync-Friendly Design Checklist
✓ No cross-region foreign keys (use global ID only)
✓ Version counters on master and replica for conflict detection
✓ Event log publishes all changes to message queue (Kafka)
✓ Idempotent upserts (same change applied twice = no conflict)
✓ Last-write-wins with vector clocks if concurrent edits possible
✓ Eventual consistency guaranteed; conflicts surfaced for human review
11Neo4j Graph Modeling for Domain Context Mapping
Graph databases excel at modeling domain context maps: domains (nodes), entities (nodes), ownership relationships (edges). This enables queries like "Which systems consume the Customer entity?" instantly.
-- Query: All consumers of Customer entityMATCH (domain : Domain)-[:CONSUMES]-(entity : Entity { name: 'Customer' })
RETURNdomain.name, entity.table_name;
Output: Sales, Marketing, Finance, Customer Service all depend on Customer. Sales owns it.
-- Query: What's the update lag for EMEA Marketing to get Finance's data?MATCH (finance : Domain { name: 'Finance' })-[:CONSUMES { update_lag: lag }]-(entity : Entity)
MATCH (marketing : Domain { name: 'Marketing' })-[:CONSUMES]-(entity)
RETURNentity.name, lag;
Graph models make context mapping visible and queryable. You can detect conflicts ("Two domains own the same entity"), trace dependencies ("If Sales.Order changes, who's affected?"), and audit ownership transitions.
Graph Modeling Benefits
• Instant visibility: "Who owns Customer?" → one query, not a governance document.
• Dependency analysis: "If we modify Customer, what breaks?" → traverse CONSUMES edges.
• Conflict detection: "Two domains claim to own the same entity" → graph constraint violation.
• Audit trails: SCD Type 2 on OWNS relationships shows when ownership transferred.