Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Start HereOrdered learning track

Data Model Types and Their Boundaries

Perbedaan dan batas antara conceptual ERD, logical ERD, physical ERD, domain aggregate, API DTO, event schema, database schema, read model, materialized view, audit model, search model, cache model, dan canonical model risk.

20 min read3837 words
PrevNext
Lesson 0282 lesson track01–15 Start Here
#model-boundary#erd#dto#event-schema+1 more

Data Model Types and Their Boundaries

Kesalahan besar dalam enterprise data modelling adalah menganggap semua model harus sama bentuknya.

Dalam sistem CPQ, quote, order, catalog, billing, telco BSS/OSS, dan quote-to-cash, satu konsep bisnis bisa memiliki banyak representasi berbeda:

Quote as business concept
Quote as logical entity
Quote as aggregate root
Quote as database tables
Quote as API response
Quote as event payload
Quote as search document
Quote as reporting fact
Quote as audit target
Quote as cache value

Semua bernama “quote”, tetapi tujuan, constraint, lifecycle, dan risiko masing-masing berbeda.

Part ini fokus pada boundary antar model: kapan model boleh mirip, kapan harus berbeda, dan apa risiko jika boundary dicampur.


1. Why Model Boundaries Matter

Dalam sistem kecil, sering cukup membuat satu class:

Quote.java
  -> mapped to quote table
  -> returned as API response
  -> serialized as event
  -> used in UI

Dalam enterprise system, pola ini biasanya menjadi masalah.

Alasannya:

  • database schema butuh optimasi untuk integrity dan query,
  • API contract butuh backward compatibility,
  • event schema butuh compatibility untuk consumer lama,
  • domain aggregate butuh behavior dan invariant,
  • reporting model butuh denormalisasi,
  • search model butuh flattening,
  • audit model butuh before/after evidence,
  • cache model butuh TTL dan invalidation,
  • canonical model butuh integrasi lintas sistem.

Satu bentuk data tidak bisa optimal untuk semua kebutuhan ini.


2. Boundary Principle

Gunakan prinsip berikut:

A model should be shaped by its purpose, owner, lifecycle, consumer, and correctness requirement.

Artinya, sebelum membuat atau mengubah model, tanyakan:

1. Model ini untuk siapa?
2. Model ini source of truth atau derived view?
3. Model ini mutable atau immutable?
4. Model ini internal atau external contract?
5. Model ini transactional atau analytical?
6. Model ini synchronous atau asynchronous?
7. Model ini harus backward-compatible atau bebas berubah?
8. Model ini memerlukan audit/history?
9. Model ini memerlukan performance optimization khusus?
10. Model ini boleh stale atau harus strongly consistent?

3. Conceptual ERD

Conceptual ERD menggambarkan konsep bisnis dan hubungan besar antar konsep.

Contoh conceptual ERD untuk quote-to-cash:

erDiagram CUSTOMER ||--o{ QUOTE : requests QUOTE ||--o{ QUOTE_ITEM : contains PRODUCT_OFFERING ||--o{ QUOTE_ITEM : selected_in QUOTE ||--o| PRODUCT_ORDER : converts_to PRODUCT_ORDER ||--o{ PRODUCT_ORDER_ITEM : contains PRODUCT_ORDER_ITEM ||--o| PRODUCT_INSTANCE : creates_or_changes BILLING_ACCOUNT ||--o{ CHARGE : receives CHARGE ||--o{ INVOICE_LINE : appears_on

Conceptual ERD tidak menjawab:

  • nama table,
  • column type,
  • foreign key detail,
  • index,
  • JSONB,
  • partitioning,
  • DTO field,
  • event schema.

Conceptual ERD menjawab:

  • konsep bisnis utama,
  • relationship besar,
  • bahasa bersama,
  • scope domain,
  • kemungkinan bounded context.

Boundary Rule

Conceptual ERD jangan dipakai langsung sebagai database schema.

Failure Mode

Jika conceptual ERD langsung dijadikan table design:

  • relationship terlalu naïve,
  • lifecycle tidak muncul,
  • state transition hilang,
  • versioning tidak ada,
  • audit tidak terdesain,
  • reporting dan integration terlupakan.

4. Logical ERD

Logical ERD lebih detail daripada conceptual ERD.

Logical ERD mulai menentukan:

  • entity,
  • attribute utama,
  • relationship cardinality,
  • optionality,
  • lifecycle ownership,
  • high-level constraint,
  • candidate key,
  • versioning need.

Contoh logical quote model:

Quote
  - quoteId
  - quoteNumber
  - customerRef
  - accountRef
  - status
  - validFrom
  - validTo
  - currency
  - version

QuoteItem
  - quoteItemId
  - quoteId
  - parentQuoteItemId
  - productOfferingRef
  - action
  - quantity
  - state
  - configurationSnapshotRef

QuotePrice
  - quotePriceId
  - quoteItemId
  - priceType
  - amount
  - currency
  - sourcePriceRef
  - snapshotAt

Logical ERD belum harus menentukan:

  • PostgreSQL data type,
  • index exact,
  • table partition,
  • migration detail,
  • ORM annotation.

Boundary Rule

Logical ERD harus cukup stabil untuk diskusi domain, tetapi belum terikat penuh ke physical implementation.

Failure Mode

Jika logical ERD terlalu physical:

  • diskusi bisnis menjadi terlalu teknis,
  • BA/PO sulit review,
  • model cepat terkunci ke database decision,
  • conceptual gap tersembunyi.

Jika logical ERD terlalu vague:

  • engineer membuat interpretasi berbeda,
  • API dan DB drift,
  • lifecycle tidak jelas,
  • ownership tidak jelas.

5. Physical ERD / Database Schema Model

Physical ERD adalah implementasi database nyata.

Di PostgreSQL, physical model memperhatikan:

  • table,
  • column,
  • data type,
  • primary key,
  • foreign key,
  • unique constraint,
  • check constraint,
  • index,
  • partition,
  • schema namespace,
  • sequence/identity,
  • JSONB,
  • materialized view,
  • migration ordering.

Contoh physical concern untuk quote item:

-- Conceptual example only.
CREATE TABLE quote_item (
    id UUID PRIMARY KEY,
    quote_id UUID NOT NULL,
    parent_quote_item_id UUID NULL,
    product_offering_id UUID NOT NULL,
    action TEXT NOT NULL,
    state TEXT NOT NULL,
    quantity NUMERIC(18, 4) NOT NULL,
    created_at TIMESTAMPTZ NOT NULL,
    updated_at TIMESTAMPTZ NOT NULL,
    version INT NOT NULL,

    CONSTRAINT fk_quote_item_quote
      FOREIGN KEY (quote_id) REFERENCES quote(id),

    CONSTRAINT fk_quote_item_parent
      FOREIGN KEY (parent_quote_item_id) REFERENCES quote_item(id),

    CONSTRAINT quote_item_quantity_positive
      CHECK (quantity > 0)
);

Physical model harus menjawab:

  • bagaimana data dijaga tetap valid?
  • bagaimana query utama dijalankan?
  • bagaimana migration dilakukan?
  • bagaimana data besar dipartisi?
  • bagaimana retention/archive?
  • bagaimana locking/concurrency?

Boundary Rule

Physical model boleh berbeda dari domain model dan API model.

Failure Mode

Jika API DTO langsung dijadikan physical model:

  • table mengikuti kebutuhan UI sementara,
  • redundant fields tidak jelas source-nya,
  • contract field menjadi sulit dihapus,
  • database terlalu mudah berubah karena API request berubah.

Jika domain object langsung dijadikan table:

  • aggregate behavior hilang,
  • persistence concern bocor ke domain,
  • JPA cascade/lazy loading bisa merusak boundary,
  • schema optimization sulit.

6. Domain Aggregate Model

Domain aggregate model adalah model yang menjaga business invariant.

Contoh aggregate:

Quote Aggregate
  Root: Quote
  Children: QuoteItem, QuotePrice, QuoteDiscount, QuoteApprovalSummary
  Invariants:
    - accepted quote cannot be modified
    - submitted quote must have at least one valid quote item
    - quote cannot be accepted without approval if discount exceeds threshold
    - quote cannot be converted after expiry

Aggregate bukan sekadar struktur data. Aggregate punya behavior.

Contoh behavior:

// Conceptual example only.
quote.submit(actor, submittedAt);
quote.approve(approvalDecision);
quote.accept(customerAcceptance);
quote.convertToOrder(conversionRequest);

Domain aggregate harus bisa menolak operasi invalid.

Boundary Rule

Domain aggregate tidak harus sama dengan table structure.

Satu aggregate bisa disimpan ke banyak tabel. Satu tabel bisa mendukung beberapa read model.

Failure Mode

Jika aggregate boundary terlalu besar:

  • transaction terlalu berat,
  • concurrency conflict tinggi,
  • performance buruk,
  • deployment sulit.

Jika aggregate boundary terlalu kecil:

  • invariant cross-entity sulit dijaga,
  • data mudah inconsistent,
  • business rule tersebar.

7. API DTO Model

API DTO adalah model contract antara service dan client/consumer synchronous.

Contoh quote response DTO:

{
  "id": "q-123",
  "quoteNumber": "Q-2026-000123",
  "status": "APPROVED",
  "customer": {
    "id": "cust-456",
    "name": "Example Enterprise"
  },
  "validUntil": "2026-08-10T23:59:59Z",
  "totalPrice": {
    "amount": "12000.00",
    "currency": "USD"
  },
  "items": []
}

DTO concern:

  • naming stability,
  • backward compatibility,
  • field deprecation,
  • error representation,
  • pagination,
  • filtering,
  • security masking,
  • external ID vs internal ID,
  • null vs missing semantics,
  • enum compatibility.

Boundary Rule

API DTO tidak boleh expose internal database schema secara mentah.

Failure Mode

Jika API DTO sama dengan DB entity:

  • internal column bocor ke client,
  • sensitive field bisa terekspos,
  • schema migration menjadi breaking API change,
  • lazy-loaded relation bisa menyebabkan performance issue,
  • client bergantung pada detail internal.

8. Event Schema Model

Event schema adalah contract asynchronous.

Contoh event:

{
  "eventId": "evt-001",
  "eventType": "QuoteAccepted",
  "eventVersion": 1,
  "occurredAt": "2026-07-11T12:00:00Z",
  "aggregateType": "Quote",
  "aggregateId": "q-123",
  "correlationId": "corr-789",
  "causationId": "cmd-456",
  "payload": {
    "quoteId": "q-123",
    "quoteNumber": "Q-2026-000123",
    "customerId": "cust-456",
    "acceptedAt": "2026-07-11T12:00:00Z",
    "validUntil": "2026-08-10T23:59:59Z"
  }
}

Event schema concern:

  • event identity,
  • aggregate identity,
  • event type,
  • event version,
  • metadata,
  • payload compatibility,
  • ordering,
  • idempotency,
  • replay,
  • consumer ownership,
  • schema evolution.

Boundary Rule

Event payload bukan API response dan bukan DB row.

Event harus merepresentasikan fakta yang sudah terjadi dan durable.

Failure Mode

Jika event dipublish sebelum DB commit:

Consumer sees event.
Producer transaction rolls back.
Downstream system acts on state that does not exist.

Jika event terlalu mirip DB row:

  • consumer tahu terlalu banyak detail internal,
  • schema evolution sulit,
  • payload terlalu besar,
  • event meaning tidak jelas.

Jika event terlalu minimal:

  • consumer harus call back ke producer,
  • coupling meningkat,
  • replay sulit,
  • historical reconstruction tidak lengkap.

9. Database Schema Model

Database schema model adalah persistence truth dalam boundary service.

Database model harus memperhatikan:

  • transaction boundary,
  • consistency,
  • idempotency,
  • concurrency,
  • FK/constraint,
  • indexes,
  • retention,
  • migration,
  • query pattern.

Contoh data yang sering dipisah di database:

quote
quote_item
quote_price_snapshot
quote_discount
quote_approval
quote_audit
quote_event_outbox
quote_conversion_attempt

Padahal dalam domain/API, user hanya melihat “Quote”.

Boundary Rule

Database schema adalah internal implementation detail untuk service owner.

Consumer tidak boleh bergantung langsung pada table internal.

Failure Mode

Jika service lain membaca table langsung:

  • schema change menjadi cross-team breaking change,
  • invariant owner bisa dibypass,
  • authorization bisa dilewati,
  • stale interpretation muncul,
  • audit tidak lengkap.

10. Read Model

Read model adalah model yang dioptimalkan untuk query tertentu.

Contoh read model:

quote_summary_view
  - quote_id
  - quote_number
  - customer_name
  - account_name
  - status
  - approval_status
  - total_amount
  - currency
  - valid_until
  - owner_name
  - last_updated_at

Read model cocok untuk:

  • dashboard,
  • search result,
  • list page,
  • approval queue,
  • aging report,
  • operational support screen.

Read model boleh denormalized.

Boundary Rule

Read model boleh stale jika freshness expectation jelas.

Failure Mode

Jika read model dianggap source of truth:

  • write dilakukan ke projection,
  • stale data menjadi business decision,
  • inconsistency sulit dilacak,
  • reconciliation tidak jelas.

11. Materialized View

Materialized view adalah read model yang disimpan dan di-refresh.

Cocok untuk:

  • expensive aggregation,
  • operational report,
  • large join,
  • historical summary,
  • dashboard yang tidak butuh real-time absolute.

Concern:

  • refresh strategy,
  • freshness SLA,
  • locking,
  • incremental vs full refresh,
  • index on materialized view,
  • ownership,
  • monitoring.

Boundary Rule

Materialized view bukan pengganti transactional table.

Failure Mode

Jika freshness tidak jelas:

Support sees order as pending.
Actual order already completed.
Wrong manual intervention performed.

12. Audit Model

Audit model adalah evidence model.

Audit model berbeda dari domain model dan event model.

Contoh audit record:

{
  "auditId": "aud-001",
  "targetType": "Quote",
  "targetId": "q-123",
  "action": "DISCOUNT_CHANGED",
  "actorId": "user-789",
  "actorRole": "SALES_MANAGER",
  "reason": "Enterprise negotiation",
  "before": {
    "discountPercent": "5.00"
  },
  "after": {
    "discountPercent": "15.00"
  },
  "correlationId": "corr-123",
  "occurredAt": "2026-07-11T12:00:00Z"
}

Audit model concern:

  • before/after,
  • actor,
  • action,
  • reason,
  • source system,
  • correlation,
  • causation,
  • retention,
  • sensitive data handling,
  • queryability,
  • legal/compliance requirement.

Boundary Rule

Audit model harus menjelaskan perubahan, bukan hanya menyalin entity.

Failure Mode

Jika audit hanya “updated_by/updated_at”:

  • tidak bisa membuktikan nilai lama,
  • tidak bisa menjelaskan reason,
  • tidak bisa trace approval,
  • sulit incident investigation,
  • dispute customer sulit dibela.

13. Search Model

Search model adalah document/projection untuk kebutuhan pencarian.

Contoh quote search document:

{
  "quoteId": "q-123",
  "quoteNumber": "Q-2026-000123",
  "customerName": "Example Enterprise",
  "accountName": "Global Account",
  "status": "APPROVED",
  "owner": "Alice",
  "productNames": ["Fiber Internet", "Managed Router"],
  "totalAmount": 12000.00,
  "currency": "USD",
  "createdAt": "2026-07-11T12:00:00Z",
  "validUntil": "2026-08-10T23:59:59Z"
}

Search model concern:

  • flattening,
  • filter fields,
  • sort fields,
  • facet fields,
  • full-text fields,
  • security filtering,
  • tenant filtering,
  • reindexing,
  • freshness,
  • deletion/retention.

Boundary Rule

Search model bukan domain model.

Search document boleh berisi denormalized data yang berasal dari banyak source, tetapi source harus jelas.

Failure Mode

Jika search model tidak memperhatikan security:

  • user bisa menemukan quote/customer yang tidak boleh dilihat,
  • cross-tenant leak,
  • sensitive pricing muncul di index.

Jika reindex process buruk:

  • search result stale,
  • deleted/archived data masih muncul,
  • support mengambil tindakan pada data lama.

14. Cache Model

Cache model adalah representasi sementara untuk mempercepat akses.

Contoh cache:

catalog:offering:v42:offering-123
pricing:list:v8:enterprise-usd
customer:summary:cust-456
quote:summary:q-123

Cache model concern:

  • key design,
  • value shape,
  • TTL,
  • invalidation,
  • versioning,
  • tenant isolation,
  • ownership,
  • stale data risk,
  • fallback behavior,
  • observability.

Boundary Rule

Cache bukan source of truth.

Failure Mode

Jika catalog/pricing cache stale:

  • quote memakai offering retired,
  • price salah,
  • discount eligibility salah,
  • quote acceptance bisa dispute.

Jika cache key tidak tenant-aware:

  • cross-tenant data leak.

15. Canonical Model

Canonical model adalah model integrasi bersama.

Contoh canonical object:

CanonicalCustomer
CanonicalProduct
CanonicalOrder
CanonicalBillingAccount

Canonical model concern:

  • semantic alignment,
  • extension fields,
  • versioning,
  • mapping from local domain,
  • mapping to external system,
  • governance,
  • compatibility,
  • ownership.

Boundary Rule

Canonical model sebaiknya berada di integration boundary, bukan menggantikan bounded context model.

Failure Mode

Jika canonical model dipakai sebagai internal model semua service:

  • service kehilangan language domain sendiri,
  • model menjadi terlalu besar,
  • optional fields meledak,
  • semantic mismatch tersembunyi,
  • release coordination berat,
  • perubahan kecil berdampak enterprise-wide.

16. Boundary Mismatch Examples

16.1 API DTO = Database Entity

Problem:
  QuoteEntity returned directly as REST response.

Impact:
  Internal columns exposed.
  Lazy relations accidentally serialized.
  Sensitive fields leak.
  Schema change breaks API.

Better:

QuoteEntity -> QuoteAggregate/ReadModel -> QuoteResponseDTO

16.2 Event Payload = Full Database Row

Problem:
  ProductOrderCreated event contains every column from product_order table.

Impact:
  Consumer coupled to producer schema.
  Sensitive internal field exposed.
  Event versioning painful.
  Event meaning unclear.

Better:

Event contains stable business fact + minimal required payload + metadata.

16.3 Read Model = Source of Truth

Problem:
  Approval service updates quote_approval_summary_view directly.

Impact:
  Projection no longer derived.
  Source of truth ambiguous.
  Rebuild projection loses update.

Better:

Write to approval domain table.
Projection is rebuilt from source/event.

16.4 Cache = Contract

Problem:
  Another service depends on Redis key shape owned by Quote Service.

Impact:
  Cache refactor breaks consumer.
  No compatibility policy.
  Ownership unclear.

Better:

Expose API/event/read model contract.
Keep cache internal.

16.5 Canonical Model = Domain Model

Problem:
  Quote Service uses enterprise canonical order object internally.

Impact:
  Quote domain polluted by fields only relevant to billing/fulfillment.
  Business rules become unclear.
  Code filled with null checks and generic extensions.

Better:

Quote domain model stays local.
Canonical mapping occurs at integration boundary.

17. Model Boundary Map

flowchart TB Conceptual[Conceptual Model] Logical[Logical Model] Domain[Domain Aggregate Model] Physical[Physical Database Model] API[API DTO Model] Event[Event Schema Model] Read[Read Model] Search[Search Model] Cache[Cache Model] Audit[Audit Model] Reporting[Reporting / OLAP Model] Canonical[Canonical Integration Model] Conceptual --> Logical Logical --> Domain Logical --> Physical Domain --> API Domain --> Event Physical --> Read Physical --> Audit Read --> Search Read --> Reporting Physical --> Cache Event --> Reporting Domain --> Canonical API --> Canonical Event --> Canonical

Diagram ini bukan urutan teknis mutlak. Ini menunjukkan bahwa setiap model punya boundary dan mapping.


18. CPQ/Quote/Order/Catalog/Billing Boundary Examples

18.1 Product Offering

Model TypeRepresentation
ConceptualSellable thing offered to customer
LogicalProductOffering entity with version, status, validity
Physicalproduct_offering, product_offering_price, product_offering_relationship tables
APIProduct offering response with selected visible fields
EventProductOfferingPublished, ProductOfferingRetired
SearchFlattened offering document with category, tags, eligibility hints
CacheVersioned offering cache by catalog version
ReportingOffering usage count in quote/order
AuditWho changed offering status/price/relation

18.2 Quote

Model TypeRepresentation
ConceptualCommercial proposal to customer
LogicalQuote, QuoteItem, QuotePrice, QuoteApproval
PhysicalMultiple normalized tables with status, version, snapshot
APIQuote detail/list/summary DTO
EventQuoteCreated, QuoteSubmitted, QuoteAccepted, QuoteConverted
SearchQuote searchable by number, customer, owner, status, product
CacheQuote summary for UI or workflow lookup
ReportingQuote funnel, aging, conversion rate
AuditPrice/discount/status/approval changes

18.3 Product Order

Model TypeRepresentation
ConceptualExecution instruction created from accepted quote
LogicalProductOrder, ProductOrderItem, OrderMilestone, OrderDependency
PhysicalOrder tables, item tables, status history, integration attempt
APIProduct order create/status response
EventProductOrderCreated, OrderItemCompleted, OrderFalloutRaised
SearchSearchable order document
CacheOrder status cache
ReportingStuck order, fallout rate, SLA aging
AuditState transitions, manual intervention, cancellation/amendment

18.4 Billing Account

Model TypeRepresentation
ConceptualAccount responsible for billing/invoice/payment
LogicalBillingAccount, BillingProfile, PaymentMethodRef, TaxProfile
PhysicalBilling account/profile tables or external reference tables
APIBilling account reference/summary
EventBillingAccountCreated, BillingProfileUpdated
SearchCustomer/billing account lookup
CacheBilling account summary cache
ReportingInvoice aging, account revenue
AuditBilling profile changes, tax/payment preference changes

19. Mapping Layer

Mapping layer adalah komponen yang menerjemahkan antar model.

Contoh mapping:

Request DTO -> Command
Command -> Domain Aggregate operation
Domain Aggregate -> Persistence rows
Persistence rows -> Domain Aggregate
Domain Event -> Outbox Event row
Domain/Read Model -> Response DTO
Persistence/Event -> Reporting projection

Mapping layer harus eksplisit agar:

  • contract tidak bocor,
  • sensitive field bisa difilter,
  • enum bisa diterjemahkan,
  • backward compatibility bisa dijaga,
  • internal refactoring aman,
  • audit dan event metadata bisa ditambahkan.

20. Boundary Review Checklist

Saat melihat model baru, tanyakan:

1. Ini model jenis apa?
2. Siapa consumer model ini?
3. Apakah model ini internal atau external contract?
4. Apakah model ini source of truth atau derived?
5. Apakah model ini boleh stale?
6. Apakah model ini mutable atau immutable?
7. Apakah field ini berasal dari domain, persistence, API, event, atau report?
8. Apakah ada sensitive field yang bocor ke API/event/search/log/cache?
9. Apakah model ini punya lifecycle sendiri?
10. Apakah model ini punya versioning policy?
11. Apakah model ini punya owner?
12. Apakah ada mapping layer eksplisit?
13. Apakah perubahan model ini breaking untuk consumer?
14. Apakah migration/backfill diperlukan?
15. Apakah audit/reporting/search/cache terdampak?

21. Boundary Smell Catalog

Smell 1: DTO Has Database Column Names

Contoh smell:

{
  "quote_hdr_id": "...",
  "quote_stat_cd": "...",
  "upd_ts": "..."
}

Kemungkinan masalah:

  • API bocor dari physical schema,
  • naming tidak domain-oriented,
  • client akan bergantung pada detail internal.

Smell 2: Event Contains UI-Only Fields

Contoh:

{
  "eventType": "QuoteSubmitted",
  "payload": {
    "buttonLabel": "Submit Quote",
    "screenSection": "approvalPanel"
  }
}

Kemungkinan masalah:

  • event polluted by presentation concern,
  • consumer bingung semantics,
  • UI refactor memengaruhi event.

Smell 3: Database Table Has External API Version Fields Everywhere

Contoh:

api_v1_field_name
api_v2_field_name
legacy_response_flag

Kemungkinan masalah:

  • persistence model tercemar contract compatibility,
  • mapping layer kurang,
  • versioning API salah tempat.

Smell 4: Search Index Is Used for Business Validation

Contoh:

Before accepting quote, service checks search index to see whether customer is active.

Kemungkinan masalah:

  • search index stale,
  • validation tidak strongly consistent,
  • quote acceptance bisa salah.

Smell 5: Cache Key Used as External Identifier

Contoh:

catalog:tenant-1:v42:offering-123 exposed to client

Kemungkinan masalah:

  • internal cache structure menjadi API contract,
  • cache refactor breaking,
  • tenant/version detail bocor.

22. Java/JAX-RS Implementation Boundary

Dalam Java service, boundary bisa dijaga dengan package/layer yang jelas.

Contoh struktur konseptual:

com.company.quote.api
  QuoteResource
  QuoteRequestDto
  QuoteResponseDto

com.company.quote.application
  SubmitQuoteCommand
  SubmitQuoteHandler
  QuoteApplicationService

com.company.quote.domain
  Quote
  QuoteItem
  QuoteStatus
  QuoteInvariantViolation
  QuoteAcceptedEvent

com.company.quote.persistence
  QuoteRepository
  QuoteMapper
  QuoteEntity
  QuoteItemEntity

com.company.quote.integration
  QuoteEventPublisher
  QuoteOutboxRecord
  BillingClient

com.company.quote.reporting
  QuoteSummaryProjection

Boundary rule:

API layer should not return persistence entity.
Domain layer should not depend on JAX-RS DTO.
Event schema should not be generated blindly from entity.
Reporting projection should not be used as command model.

23. MyBatis/JPA/JDBC Boundary Concerns

MyBatis

MyBatis membuat SQL eksplisit. Ini bagus untuk read model dan complex query.

Boundary concern:

  • mapper result tidak otomatis domain aggregate,
  • query projection jangan dipakai sebagai write model,
  • SQL result harus jelas consumer-nya,
  • DTO mapping jangan dicampur dengan persistence row mapping.

JPA

JPA sering menggoda engineer untuk menjadikan entity sebagai domain model sekaligus persistence model.

Boundary concern:

  • lazy loading bocor ke API,
  • cascade bisa menulis data tanpa domain invariant,
  • bidirectional relation membuat aggregate boundary kabur,
  • generated schema bukan pengganti data modelling.

JDBC

JDBC memberi kontrol, tetapi boundary harus manual.

Boundary concern:

  • row mapper harus jelas output-nya,
  • transaction boundary explicit,
  • mapping duplication harus dikendalikan,
  • SQL error harus diterjemahkan ke domain/application error.

24. Event and API Compatibility Boundaries

API dan event sama-sama contract, tetapi compatibility-nya berbeda.

ConcernAPIEvent
Interactionsynchronousasynchronous
Consumerclient/service callerevent consumer
Failureimmediate responseretry/DLQ/replay
Versioningendpoint/media/field compatibilityschema/event version
Orderingper requestper key/partition/queue semantics
Staterequest-time viewfact after state change
Replayusually nooften yes

Jangan menganggap event schema bisa sama dengan API response.

API response sering menjawab:

What is the current view of this resource?

Event menjawab:

What fact just happened?

25. Reporting Boundary

Reporting model sering membutuhkan data lintas bounded context.

Contoh report:

Quote conversion rate by product family and sales channel.

Data yang dibutuhkan:

  • quote,
  • quote item,
  • product offering,
  • product family,
  • sales channel,
  • order conversion,
  • time dimension.

Jika report langsung join semua OLTP table lintas service, boundary rusak.

Alternatif:

  • event-driven projection,
  • reporting database,
  • materialized read model,
  • ETL/ELT pipeline,
  • fact/dimension model.

Reporting boleh duplicate data, tetapi harus jelas:

  • source,
  • freshness,
  • transformation logic,
  • reconciliation method,
  • ownership.

26. Audit Boundary

Audit model tidak boleh hanya dianggap sebagai event model.

Domain event:

QuoteAccepted

Audit event:

Actor Alice accepted Quote Q-123 through UI using customer acceptance evidence E-456 at time T.
Before status APPROVED, after status ACCEPTED.

Event memberi tahu sistem lain bahwa fakta terjadi. Audit menjelaskan bukti perubahan.

Keduanya bisa dibuat dari action yang sama, tetapi modelnya berbeda.


27. Security Boundary

Model boundary harus mempertimbangkan data exposure.

Sensitive fields bisa aman di database tetapi tidak aman di:

  • API response,
  • event payload,
  • search index,
  • cache value,
  • logs,
  • audit before/after,
  • reporting export.

Contoh sensitive data:

  • customer contact,
  • billing address,
  • payment reference,
  • contract terms,
  • negotiated discount,
  • margin/cost,
  • approval comment,
  • internal fallout reason,
  • tenant identifier,
  • external system credential/reference.

Boundary review harus selalu bertanya:

Should this field cross this boundary?

28. Performance Boundary

Setiap model punya performance concern berbeda.

ModelPerformance Concern
Domain aggregatetransaction size, loading strategy, locking
Physical DBindex, join, partition, constraint cost
API DTOpayload size, pagination, nested expansion
Eventpayload size, partition key, consumer throughput
Read modelrefresh cost, denormalization correctness
Search modelindexing lag, query latency, security filtering
Cache modelhit ratio, invalidation, stale risk
Audit modelwrite volume, retention, queryability

Performance optimization di satu model bisa merusak correctness di model lain jika tidak hati-hati.


29. Model Drift

Model drift terjadi saat representasi data antar layer perlahan tidak sinkron secara semantic.

Contoh drift:

Database status: APPROVED
API status: READY_FOR_ACCEPTANCE
Event status: APPROVED_PENDING_CUSTOMER
UI label: Approved
Report bucket: Submitted

Mungkin semua terlihat benar secara lokal, tetapi secara enterprise membingungkan.

Penyebab drift:

  • mapping tidak eksplisit,
  • enum tidak terdokumentasi,
  • backward compatibility patch tanpa cleanup,
  • reporting transformation berbeda,
  • event version lama masih dipakai,
  • business terminology berubah.

Mitigasi:

  • glossary,
  • mapping table,
  • contract tests,
  • state transition docs,
  • data dictionary,
  • schema governance,
  • observability metric.

30. Boundary Decision Matrix

Gunakan matrix ini saat mendesain model.

QuestionConceptualLogicalPhysicalAPIEventRead/SearchAudit
Business meaningHighHighMediumMediumHighMediumHigh
Implementation detailLowMediumHighMediumMediumHighMedium
External compatibilityLowLowLowHighHighMediumMedium
Query performanceLowMediumHighMediumMediumHighMedium
Historical evidenceMediumMediumMediumLowMediumMediumHigh
MutabilityConceptualControlledControlledContractualImmutable factDerivedAppend-oriented
OwnerBusiness/domainDomain architectService/DB ownerAPI ownerEvent ownerProjection ownerAudit owner

31. Internal Verification Checklist

Gunakan checklist ini untuk memeriksa boundary model di codebase dan dokumentasi internal.

Conceptual / Logical Model

  • Apakah ada ERD konseptual?
  • Apakah ada logical ERD?
  • Apakah entity relationship dijelaskan dengan business meaning?
  • Apakah cardinality dan optionality jelas?
  • Apakah lifecycle entity utama terdokumentasi?
  • Apakah istilah domain konsisten dengan BA/PO/architect?

Physical Model

  • Apakah table design sesuai logical model?
  • Apakah ada constraint yang menjaga invariant dasar?
  • Apakah index sesuai query pattern?
  • Apakah JSONB/EAV digunakan dengan alasan jelas?
  • Apakah migration scripts terdokumentasi?
  • Apakah table history/audit tersedia?

Domain Model

  • Apakah aggregate root jelas?
  • Apakah business invariant ada di domain/application layer?
  • Apakah state transition centralized?
  • Apakah domain model bebas dari JAX-RS DTO?
  • Apakah domain model bebas dari persistence-specific leakage?

API Model

  • Apakah DTO terpisah dari DB entity?
  • Apakah API field punya backward compatibility policy?
  • Apakah sensitive field dimasking?
  • Apakah enum API terdokumentasi?
  • Apakah pagination/filter/sort contract jelas?

Event Model

  • Apakah event schema terpisah dari API DTO?
  • Apakah event punya metadata wajib?
  • Apakah event versioned?
  • Apakah outbox digunakan?
  • Apakah consumer idempotent?
  • Apakah event ordering key jelas?

Read / Reporting Model

  • Apakah read model source-nya jelas?
  • Apakah freshness SLA jelas?
  • Apakah projection bisa direbuild?
  • Apakah reporting query tidak membebani OLTP secara berlebihan?
  • Apakah metric lifecycle punya definisi konsisten?

Search Model

  • Apakah search document hanya berisi field yang aman dicari?
  • Apakah tenant/security filtering diterapkan?
  • Apakah reindex process tersedia?
  • Apakah stale search result dimonitor?

Cache Model

  • Apakah cache key internal dan tidak menjadi contract?
  • Apakah TTL/invalidation jelas?
  • Apakah cache tenant-aware?
  • Apakah stale data failure mode terdokumentasi?

Audit Model

  • Apakah audit before/after tersedia untuk field penting?
  • Apakah actor/action/reason/correlation dicatat?
  • Apakah approval trace tersambung?
  • Apakah audit retention policy jelas?

Canonical Model

  • Apakah canonical model hanya dipakai di integration boundary?
  • Apakah local domain model tetap independen?
  • Apakah extension field terkendali?
  • Apakah semantic mismatch didokumentasikan?

32. PR Review Checklist for Model Boundaries

Saat review PR, gunakan pertanyaan berikut:

1. Model apa yang berubah: DB, API, event, domain, read, search, cache, audit, atau reporting?
2. Apakah perubahan di satu model membutuhkan perubahan mapping di model lain?
3. Apakah external contract berubah?
4. Apakah backward compatibility aman?
5. Apakah field baru perlu audit?
6. Apakah field baru boleh masuk API/event/search/cache/log?
7. Apakah migration/backfill diperlukan?
8. Apakah read model/projection perlu rebuild?
9. Apakah index perlu ditambah/diubah?
10. Apakah event consumer terdampak?
11. Apakah API client terdampak?
12. Apakah report/KPI terdampak?
13. Apakah cache invalidation terdampak?
14. Apakah data ownership tetap jelas?
15. Apakah test mencakup boundary mapping?

33. Practical Example: Adding Quote Expiry Reason

Misalkan bisnis ingin menambahkan alasan kenapa quote expired.

Conceptual Model

Quote may expire because of validity date, customer rejection, superseded revision, or manual cancellation-like business reason.

Logical Model

Tambahkan konsep:

QuoteExpiryReason
  - reasonCode
  - reasonDescription
  - source
  - actorRef
  - expiredAt

Physical Model

Opsi:

Add columns to quote:
  expiry_reason_code
  expired_at
  expired_by

Or create quote_expiry table if richer history is required.

Domain Model

quote.expire(reason, actor, time)

Guard:

Only non-terminal quote can expire.
Accepted or converted quote cannot expire.

API Model

Response may expose:

{
  "status": "EXPIRED",
  "expiryReason": "VALIDITY_PERIOD_ENDED",
  "expiredAt": "..."
}

But internal actor/source may not be exposed.

Event Model

Publish:

QuoteExpired

Payload includes stable reason code, not internal DB fields.

Audit Model

Audit records actor/source/reason/before-after status.

Reporting Model

Quote expiry funnel can group by reason.

Search Model

Quote search may filter by expired status, but maybe not by detailed internal reason.

Cache Model

Quote summary cache must be invalidated.

Boundary Lesson

Satu business change memengaruhi banyak model, tetapi setiap model menerima bentuk data yang berbeda.


34. Summary

Model boundary adalah fondasi enterprise data modelling.

Prinsip utama:

  • conceptual model menyamakan business meaning,
  • logical model merinci entity dan relationship,
  • physical model mengoptimalkan persistence dan integrity,
  • domain aggregate menjaga invariant,
  • API DTO menjaga synchronous contract,
  • event schema menjaga asynchronous contract,
  • read model mengoptimalkan query,
  • materialized view mengoptimalkan reporting tertentu,
  • audit model menyimpan evidence,
  • search model mengoptimalkan discovery,
  • cache model mengoptimalkan latency,
  • canonical model membantu integrasi tetapi bisa menjadi coupling risk.

Jangan memaksa satu bentuk data menjadi semua model.

Model boundary yang jelas membuat sistem lebih mudah berubah, lebih aman di production, lebih mudah diaudit, dan lebih mudah direkonsiliasi.


35. Learning Outcome

Setelah menyelesaikan part ini, Anda harus mampu:

  • membedakan conceptual ERD, logical ERD, physical ERD, domain aggregate, API DTO, event schema, database schema, read model, materialized view, audit model, search model, cache model, dan canonical model,
  • menjelaskan kenapa setiap model punya purpose dan boundary berbeda,
  • mendeteksi boundary mismatch dalam PR atau desain architecture,
  • menghindari penggunaan DB entity sebagai API response atau event payload,
  • memahami risiko canonical model yang terlalu dominan,
  • menilai dampak perubahan field/entity ke API, event, reporting, cache, audit, dan migration,
  • membuat checklist review untuk model mapping dan contract compatibility.

36. Prerequisite untuk Part Berikutnya

Sebelum lanjut ke Part 003, pastikan Anda sudah memahami:

  • model boundary,
  • source of truth vs derived model,
  • internal model vs external contract,
  • DTO vs entity vs event payload,
  • read model vs write model,
  • cache/search/reporting as projections,
  • audit as evidence model,
  • canonical model risk.

Part berikutnya akan masuk ke Domain-Driven Data Modelling: entity, value object, aggregate, aggregate root, bounded context, invariant, lifecycle state, domain event, dan data ownership.

Lesson Recap

You just completed lesson 02 in start here. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.