☰
DDD β€” Panduan
Materi Pembelajaran

Domain Driven Design

Membangun software yang mencerminkan bisnis β€” bukan asumsi programmer.

πŸ›οΈ
Mengapa DDD?
Developer membuat sistem yang secara teknis benar, tapi secara bisnis salah. DDD menyelesaikan ini.
ANALOGI β€” ARSITEK DAN KLIEN
Klien bilang "saya mau rumah yang nyaman". Kalau arsitek langsung bangun tanpa banyak tanya, hasilnya mungkin bagus menurut arsitek tapi salah menurut klien. DDD mengajarkan programmer untuk ngobrol dalam, sering, dan dalam bahasa yang sama dengan bisnis.
🚫 Masalah Klasik
Fitur tidak sesuai kebutuhan. Nama variabel berbeda dengan istilah bisnis. Komunikasi putus di tengah jalan.
βœ… Solusi DDD
Bangun software berdasarkan domain β€” masalah bisnis nyata. Teknologi menyesuaikan, bukan sebaliknya.
πŸ’Ž INTI DDD
DDD bukan tentang teknologi. DDD tentang memahami masalah bisnis lalu menerjemahkannya ke dalam kode. DDD dibagi dua sisi: Strategic Design (domain, bounded context, context map) dan Tactical Design (entity, aggregate, event, repository).
2003
Diperkenalkan oleh Eric Evans
Strategic
Domain, Context, Language
Tactical
Entity, Aggregate, Event
Domain & Sub-domain
Domain = bidang masalah bisnis. Sub-domain = pecahannya yang lebih spesifik.
ANALOGI β€” RESTORAN
Dapur (memasak), kasir (pembayaran), pelayan (melayani tamu), gudang (stok). Masing-masing adalah sub-domain dengan aturan sendiri, tapi bekerja untuk satu tujuan.
Sub-domain sebuah Restoran
🍳
Dapur
Core Domain
πŸ’³
Kasir
Supporting
πŸ“¦
Stok Bahan
Supporting
πŸ§‘β€πŸ€β€πŸ§‘
Pelayan
Supporting
πŸ—“οΈ
Reservasi
Generic
⭐
Ulasan
Generic
TIPE 01

Core Domain

Inti bisnis. Keunggulan kompetitif. Harus dibangun sendiri dengan sangat baik.

TIPE 02

Supporting Domain

Mendukung core, bukan yang utama. Bisa custom tapi tak perlu sempurna.

TIPE 03

Generic Domain

Masalah umum, pakai solusi jadi. Contoh: login, email, kalender.

KUNCI

Domain Model

Peta aturan bisnis yang bisa dipahami semua pihak. Bukan kode β€” tapi representasi konseptual.

Ubiquitous Language
Satu kata yang sama, digunakan oleh semua orang β€” bisnis, manajer, programmer β€” dengan arti yang sama persis.
🚫 TANPA UBIQUITOUS LANGUAGE
  • Bisnis: "Pelanggan"
  • Database: tabel "user"
  • Code: variabel "person"
  • API: field "client"
  • Semua sama, nama beda β†’ bingung!
βœ… DENGAN UBIQUITOUS LANGUAGE
  • Bisnis: "Pelanggan"
  • Database: tabel "pelanggan"
  • Code: class Pelanggan
  • API: field "pelanggan"
  • Satu kata, satu makna!
Cara membangun Ubiquitous Language
Ngobrol dengan domain expert
β†’
Catat istilah penting
β†’
Sepakati definisi
β†’
Pakai di kode & dokumen
Jika istilah berbeda antar departemen, mungkin maknanya memang berbeda β€” dan itu sinyal adanya dua Bounded Context.
Bounded Context
Dalam sistem besar, kata yang sama bisa berarti hal berbeda di area berbeda. Bounded Context adalah "pagar" yang memastikan satu kata = satu makna di dalamnya.
ANALOGI β€” KATA "PESANAN" DI RESTORAN
Di restoran yang sama, kata "pesanan" punya arti berbeda tergantung siapa yang bicara:

🍳 Dapur: pesanan = daftar menu yang harus dimasak + instruksi (tanpa sambal, extra pedas)
πŸ§‘β€πŸ€β€πŸ§‘ Pelayan: pesanan = nomor meja, nama tamu, status antaran (sudah / belum)
πŸ’³ Kasir: pesanan = total harga, metode bayar, kembalian, diskon
πŸ“¦ Stok: pesanan = bahan yang terpakai (telur -2, nasi -1 porsi)

Kata sama, data dan makna beda! Bounded Context memberi pagar: di "Dapur", pesanan hanya tentang masak. Di "Kasir", pesanan hanya tentang uang.
Restoran β€” Bounded Contexts:
🍳 Dapur
Menu, Resep, Antrian Masak
πŸ’³ Kasir
Harga, Bayar, Kembalian
πŸ“¦ Stok
Bahan, Supplier, Restock
πŸ§‘β€πŸ€β€πŸ§‘ Pelayan
Meja, Tamu, Antaran
πŸ—“οΈ Reservasi
Jadwal, Kapasitas
⭐ Ulasan
Rating, Komentar
Satu kata, beda makna per Context:
Kata 🍳 Dapur πŸ’³ Kasir πŸ§‘β€πŸ€β€πŸ§‘ Pelayan πŸ“¦ Stok
"Pesanan" Daftar menu + instruksi masak Total harga + metode bayar Nomor meja + status antar Bahan yang terpakai
"Item" Hidangan yang harus dimasak Baris di struk (nama + harga) Piring yang harus diantar Bahan baku (telur, beras)
"Pelanggan" Preferensi alergi / rasa Nama di invoice / member Tamu di meja nomor X β€” (tidak relevan)
Setiap kolom = satu Bounded Context. Data, aturan, dan makna masing-masing independen.
πŸ”‘ HUBUNGAN DOMAIN ↔ BOUNDED CONTEXT
Satu sub-domain (area masalah) biasanya dipetakan ke satu Bounded Context (solusi teknis). Sub-domain "Dapur" diimplementasi di Bounded Context "Dapur" dengan Entity, event, dan aturan bisnisnya sendiri β€” terisolasi dari konteks lain.
🎯 Manfaat
Tim Dapur bisa berkembang tanpa mengganggu tim Kasir. Kode lebih bersih. Perubahan di satu konteks tidak merusak yang lain.
πŸ—ΊοΈ Context Map
Peta hubungan antar Bounded Context. Tipe relasi: Shared Kernel (berbagi model), Customer-Supplier (upstream-downstream), Anti-Corruption Layer (filter data masuk).
Building Blocks
Komponen taktis untuk membangun domain model.
01
Entity

Entity

Objek dengan identitas unik yang persisten. Walau atributnya berubah, tetap objek yang sama. Contoh: Pesanan #001 berubah status tapi tetap pesanan itu.

02
Value Object

Value Object

Tanpa identitas. Didefinisikan oleh nilainya saja dan bersifat immutable. Contoh: "Rp 50.000" β€” dua VO dengan nilai sama dianggap identik.

03
Aggregate

Aggregate & Aggregate Root

Cluster Entity + Value Object sebagai satu consistency boundary. Aggregate Root = satu-satunya entry point. Semua perubahan harus lewat Root.

04
Repository

Repository

Abstraksi untuk persistence β€” simpan dan ambil Aggregate. Kode domain tidak perlu tahu detail DB (SQL, NoSQL, file).

05
Domain Event

Domain Event

Fakta yang sudah terjadi di domain. Bersifat immutable, past-tense. Contoh: PesananDibuat, PembayaranDiterima.

06
Domain Service

Domain Service

Operasi bisnis yang melibatkan beberapa Aggregate dan tidak bisa diletakkan di satu Entity. Contoh: TransferUang antara 2 rekening.

Contoh: Sistem Pemesanan Pizza
Entity: Pesanan #001 VO: Harga Rp 80.000 Root: Pesanan (berisi Items) Repo: PesananRepository Event: PesananMasukDapur Service: HitungDiskon
Layered Architecture
Kode diorganisasi ke dalam lapisan. Logic bisnis terisolasi di pusat (Domain). Dependency mengarah ke dalam.
4 β€” Interface (API / Controllers)TERLUAR
3 β€” Infrastructure (DB, Messaging, External)TEKNIS
2 β€” Application (Use Cases, Orchestration)KOORDINASI
1 β€” Domain (Entities, Business Rules)INTI ❀️
LAYER 01
Domain

Domain Layer

Jantung sistem. Entity, VO, Aggregate, Event, Domain Service. Tidak bergantung pada layer lain.

LAYER 02
Application

Application Layer

Mengatur use case. Menerima command/query, memanggil domain, return result. Tidak berisi logic bisnis β€” hanya orkestrasi.

LAYER 03
Infrastructure

Infrastructure Layer

Implementasi teknis: koneksi DB, message broker, email. Mengimplementasi interface dari Domain/Application.

LAYER 04
Interface

Interface Layer

Titik masuk dari dunia luar: REST API, gRPC, WebSocket. Terjemahkan HTTP request menjadi command/query.

⚑ DEPENDENCY RULE
Dependency selalu mengarah ke dalam. Domain tidak kenal Infrastructure maupun Interface (Dependency Inversion Principle). Ini memungkinkan penggantian DB tanpa mengubah logic bisnis.
Event Sourcing
Simpan semua event yang terjadi, bukan hanya state terakhir. State direkonstruksi dengan replay.
ANALOGI β€” BUKU TABUNGAN
Saldo rekening = hasil semua transaksi. Simpan semua "transaksi" (event), saldo (state) bisa dihitung kapan saja dari awal.
πŸ—ƒοΈ CRUD TRADISIONAL
  • Simpan hanya state terakhir
  • Data: { saldo: 500.000 }
  • Riwayat hilang
  • Sulit di-audit
πŸ“œ EVENT SOURCING
  • Simpan semua event
  • Setor 1.000.000
  • Tarik 300.000
  • Transfer 200.000
  • Replay β†’ saldo 500.000 βœ“
Command
β†’
Aggregate
β†’
Event
β†’
Apply()
β†’
Event Store
C# β€” Aggregate Root + Event Sourcing (CTAS)
public class CaseAggregate : AggregateRoot<CaseAggregateState> { public CaseCreatedEvent StartNewCase(ICreateCaseRequest cmd) { var @event = new CaseCreatedEvent(); @event.CaseNumber = caseNumber; @event.TaxpayerIdentifier = cmd.TaxpayerIdentifier; Apply(@event); // mutate state + store event return @event; } }
βœ… Keuntungan
β€’ Audit trail lengkap
β€’ Time-travel debugging
β€’ Event replay untuk rebuild state
β€’ Cocok untuk keuangan & perpajakan
⚠️ Tantangan
β€’ Lebih kompleks dari CRUD
β€’ Event store membesar β†’ butuh Snapshot
β€’ Event versioning (upcasting)
β€’ Read model eventually consistent
CQRS
Command Query Responsibility Segregation β€” pisahkan model tulis dari model baca.
✏️ COMMAND (WRITE)
  • Mengubah state
  • Validasi bisnis ketat
  • Lewat Aggregate & Domain
  • CreateCaseCommand
  • UpdateTaxpayerCommand
πŸ” QUERY (READ)
  • Hanya membaca
  • Dioptimalkan untuk performa baca
  • Bisa pakai Read Model / Projection
  • GetCaseByIdQuery
  • SearchTaxpayerQuery
Client
β†’
Command / Query
β†’
Handler
β†’
Domain / Read Model
β†’
Response
πŸš€ KAPAN PAKAI CQRS?
Cocok ketika: beban baca ≫ tulis, model baca dan tulis sangat berbeda, butuh scale yang berbeda, atau dikombinasikan dengan Event Sourcing. Untuk CRUD sederhana, CQRS berlebihan.
Saga Pattern
Mengkoordinasikan proses bisnis multi-langkah lintas service. Jika satu langkah gagal, saga menjalankan compensating action.
ANALOGI β€” PESAN TIKET PESAWAT
(1) Reserve kursi, (2) potong saldo, (3) kirim tiket email, (4) catat di maskapai. Kalau langkah ke-3 gagal, Saga rollback: uang dikembalikan, kursi dibuka lagi.
πŸ“ Create
β†’
πŸ“Ž Upload
β†’
πŸ‘€ Assign
β†’
πŸ“Š Update
β†’
βœ… Done
C# β€” MassTransit StateMachine (CTAS)
public class CaseTypeCreationStateMachine : MassTransitStateMachine<CaseTypeCreationState> { public State Creating { get; set; } public State DocumentUploading { get; set; } public State Completed { get; set; } public State Faulted { get; set; } During(Creating, When(CreationCompleted) .TransitionTo(DocumentUploading)); }
πŸ“‹ Orchestration
Satu koordinator pusat (saga orchestrator) mengatur semua langkah dan mengirim compensating command jika gagal. CTAS pakai ini via MassTransit StateMachine.
πŸ”„ Choreography
Tanpa koordinator pusat. Setiap service dengar event lalu bertindak sendiri. Lebih loose-coupled, tapi lebih sulit di-trace dan di-debug.
Tactical Patterns Lanjutan
Pattern teknis yang umum di proyek DDD skala besar. Semua ditemukan di codebase CTAS.
PATTERN 01
Factory

Aggregate Factory

Membuat instance Aggregate baru dengan identitas unik. Memisahkan logika pembuatan dari Aggregate itu sendiri β€” sesuai Single Responsibility Principle.

CTAS β€” CaseAggregateFactory
public class CaseAggregateFactory : ICaseAggregateFactory { public CaseAggregate CreateAggregate() => new CaseAggregate { AggregateIdentifier = Guid.NewGuid() }; }
PATTERN 02
Snapshot

Snapshot Strategy

Optimasi Event Sourcing. Simpan "foto" state di titik tertentu agar tidak perlu replay ribuan event dari awal. CTAS menggunakan SnapshotStrategy<T> di modul Accounting.

PATTERN 03
Projection

Read Model / Projection

Consumer yang mendengarkan Domain Event lalu membangun model baca yang dioptimasi untuk query. Ini bagian "Q" dari CQRS. Contoh di CTAS: TaxpayerForTaxTypeReadModelEventConsumer.

PATTERN 04
Event Store

Event Store

Penyimpanan khusus event. CTAS punya TransactionEventStore di Accounting dengan operasi: save, replay, snapshot check, dan boxing (archival ke offline storage).

PATTERN 05
Outbox

Outbox Pattern

Event disimpan di tabel outbox bersamaan dengan perubahan data (1 transaksi DB atomik). Background worker mengirim ke message broker. Menjamin konsistensi data & pesan.

PATTERN 06
Lock

Distributed Locking

Mencegah race condition di sistem terdistribusi. CTAS pakai Redis Redlock via ILockHelper.Lock("resource"). Hanya satu proses boleh akses resource secara bersamaan.

PATTERN 07
Validation

Command Validation

Validasi command sebelum masuk domain logic. CTAS pakai FluentValidation β€” class AbstractValidator<TCommand> di Domain/Commands/Validator/.

PATTERN 08
Gateway

API Gateway

Single entry-point untuk semua request dari client. CTAS menggunakan Ocelot sebagai reverse-proxy yang mengarahkan ke 20+ microservice berdasarkan route config.

πŸ”— MIDDLEWARE PIPELINE
CTAS menggunakan middleware chain: CorrelationLog (trace distributed request), HMAC (autentikasi antar service), SecurityPayload (enkripsi), dan MassTransit Middleware (message filter & retry).
Case Study: CTAS
Core Tax Administration System β€” implementasi DDD skala besar di .NET 6 untuk sistem perpajakan.
20+
Bounded Context
4 Layer
Clean Architecture
RabbitMQ
MassTransit
Context Map:
πŸ“‹
Registration
Wajib Pajak
πŸ“‚
CaseMgmt
Kasus
πŸ’°
Payment
Pembayaran
πŸ“Š
Accounting
Akuntansi
πŸ“
ReturnSheet
SPT
πŸ”
Auth
Otorisasi
πŸ“„
DocMgmt
Dokumen
πŸ””
Notif
Pemberitahuan
🏒
Collections
Penagihan
πŸ”
Audit
Pemeriksaan
πŸ“ˆ
BI
Reporting
🌐
E-Invoice
Faktur
🏦
EoI
Exchange of Info
πŸ“‰
CRM
Risk Mgmt
πŸ”¬
DQM
Data Quality
Pattern yang Digunakan:
πŸ“ Clean Architecture β€” 4 Layerβ–Ό
Setiap bounded context diorganisasi: 1-Domain/ β†’ 2-Application/ β†’ 3-Infrastructure/ β†’ 4-Interface/
Folder Structure
Registration/ β”œβ”€β”€ 1-Domain/ β†’ Commands, Model, Queries, Sagas, Services β”œβ”€β”€ 2-Application/ β†’ Consumers, Events, Repositories, Services β”œβ”€β”€ 3-Infrastructure/ β†’ DB, External Service calls └── 4-Interface/ β†’ REST API (WebApi)
🧱 Aggregate Root + Event Sourcingβ–Ό
Pattern AggregateRoot<TState>. Semua mutasi lewat Apply(event). State di-maintain oleh generic TState class.

Aggregates di CTAS: CaseAggregate, CaseTypeAggregate, TransactionAggregate, BillingAggregate, PaymentOrderAggregate, NtpnPaymentAggregate.
πŸ”€ Saga / StateMachine (MassTransit)β–Ό
CaseTypeCreationStateMachine Β· DocumentUploadStateMachine Β· DocumentDeletionStateMachine Β· DeregistrationStateMachine Β· ExOfficioVatRegistrationStateMachine Β· SubmitApprovalBalanceTransferStateMachine Β· PmseDataUpdateApproveStateMachine
πŸ“¨ Consumer / Publisher + Shared Contractsβ–Ό
Komunikasi antar context via RabbitMQ (MassTransit). Shared.Contracts = 50+ sub-folder kontrak (Accounting, Cases, Payment, Registration, dll) β€” ini adalah Ubiquitous Language antar service.

Outbox Pattern: event β†’ outbox table (atomik) β†’ worker β†’ RabbitMQ β†’ consumer.
πŸ“Έ Snapshot + Event Storeβ–Ό
TransactionEventStore di Accounting: save, replay, snapshot check, boxing (archive ke offline storage).
TransactionSnapshotStrategy : SnapshotStrategy<T> β€” auto-snapshot setiap N event.
AccountingSnapshot : Snapshot β€” serialized state pada titik tertentu.
πŸ“Š Read Model Projectionsβ–Ό
Consumer khusus yang build read model dari event stream (CQRS query side):
TaxpayerForTaxTypeReadModelEventConsumer Β· BankDetailReadModelEventConsumer Β· ContactReadModelEventConsumer Β· FamilyMemberReadModelEventConsumer Β· AppointmentReadModelEventConsumer Β· LandAndBuildingReadModelConsumer
πŸ”’ Distributed Lock + Validation + Gatewayβ–Ό
Redlock (Redis): ILockHelper.Lock("resource") β€” mencegah race condition lintas instance.
FluentValidation: AbstractValidator<TCommand> di Domain/Commands/Validator/.
Ocelot API Gateway β€” single entry point routing ke 20+ microservice.
Middleware Pipeline: CorrelationLog β†’ HMAC β†’ SecurityPayload β†’ handler.
Quiz
Uji pemahaman tentang konsep DDD. Pilih jawaban yang paling tepat!