Episode ini membahas arsitektur aplikasi .NET: clean architecture dan layered architecture, dependency injection dengan container bawaan, CQRS dan mediator pattern untuk pemisahan perintah dan query, serta dasar DDD dan organisasi solution yang modular.

Codebase yang besar akan terus tumbuh. Tanpa arsitektur yang jelas, setiap fitur baru membuat kode semakin sulit dipahami, diuji, dan diubah. Arsitektur adalah cara mengelola kompleksitas itu dengan disiplin.
Di .NET, prinsip-prinsip ini sudah menjadi praktik umum: pemisahan lapisan, injection dependency, pemisahan perintah dan query, serta pemodelan domain yang dekat dengan bisnis.
Episode 16 ini mengajarkan clean architecture dan layered architecture, dependency injection, CQRS dengan mediator, serta dasar Domain-Driven Design dan organisasi solution yang modular.
Layered architecture membagi aplikasi menjadi lapisan: Presentation, Application, Domain, dan Infrastructure. Aturan utamanya: ketergantungan mengarah ke dalam — lapisan dalam tidak boleh tahu apa-apa tentang lapisan luar.
Clean architecture mempertegas dengan memusatkan semuanya pada Domain sebagai inti yang paling stabil. Rincian seperti database dan framework ditempatkan di pinggiran sehingga mudah diganti:
Controllers/Api → Application → Domain
\ |
-------- InfrastructureDengan struktur ini, aturan bisnis di Domain tidak terikat ke EF Core, ASP.NET Core, atau NuGet tertentu. Kalian bisa menguji aturan bisnis tanpa database sama sekali.
Dependency injection (DI) membuat class tidak menciptakan dependensinya sendiri, melainkan menerimanya lewat constructor. Container bawaan .NET mendaftarkan dan mengatur siklus hidup object:
interface IPembayaran
{
Task BayarAsync(decimal jumlah);
}
class PaymentGateway : IPembayaran { }
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IPembayaran, PaymentGateway>();
class OrderService
{
private readonly IPembayaran _pembayaran;
public OrderService(IPembayaran pembayaran)
{
_pembayaran = pembayaran;
}
}AddScoped<IPembayaran, PaymentGateway> membuat satu instance per request. Karena OrderService menerima IPembayaran di constructor, ia tidak perlu tahu implementasi konkretnya — inilah yang membuat pengujian dengan mock di episode 10 menjadi mudah.
CQRS memisahkan operasi yang mengubah state (command) dari operasi yang membaca state (query). Mediator menghubungkan keduanya tanpa menyebar referensi antar handler. Package MediatR mengimplementasikan pola ini:
dotnet add src/Toko.App package MediatRrecord BuatProdukCommand(string Nama, decimal Harga) : IRequest<int>;
class BuatProdukHandler : IRequestHandler<BuatProdukCommand, int>
{
public async Task<int> Handle(
BuatProdukCommand request, CancellationToken ct)
{
var produk = new Produk { Nama = request.Nama, Harga = request.Harga };
db.Produk.Add(produk);
await db.SaveChangesAsync(ct);
return produk.Id;
}
}IMediator.Send(new BuatProdukCommand("Kopi", 85_000m)) meneruskan command ke handler yang terdaftar. Kode pemanggil hanya bergantung pada abstraksi IMediator, membuat tiap use case menjadi unit yang mandiri dan mudah diuji.
Domain-Driven Design menekankan pemodelan yang mencerminkan bahasa bisnis: entitas, value objects, dan aggregates. Setiap bagian besar bisnis dipisah dalam bounded context yang punya model dan bahasa sendiri.
Gunakan satu project per lapisan agar dependensi bisa ditegakkan oleh compiler:
dotnet new sln -n Ordering
dotnet new classlib -n Ordering.Domain -o src/Ordering.Domain
dotnet new classlib -n Ordering.Application -o src/Ordering.Application
dotnet new classlib -n Ordering.Infrastructure -o src/Ordering.Infrastructure
dotnet new webapi -n Ordering.Api -o src/Ordering.Api
dotnet sln Ordering.sln add src/Ordering.Domain src/Ordering.Application \
src/Ordering.Infrastructure src/Ordering.ApiPerintah dotnet new classlib -n Ordering.Domain menciptakan project library tanpa entry point. Karena Application hanya bisa mereferensikan Domain (bukan Infrastructure), pelanggaran arsitektur terdeteksi saat build — bukan saat production.
Inti yang harus dibawa pulang:
Di episode 17 selanjutnya kita membangun untuk skala besar: cloud-native dan microservices — prinsip microservices dan container, gRPC dan minimal APIs, service discovery dan resilience patterns dengan Polly dan MassTransit, serta observability pattern di microservices.