Belajar C# - Architecture & Design Patterns
Series/Belajar C#/Episode 16
Episode 16 of 23

Belajar C# - Architecture & Design Patterns

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.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Pendahuluan

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.

Clean Architecture dan Layered Architecture

Prinsip Ketergantungan

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:

Lapisan clean architecture
Controllers/Api  →  Application  →  Domain
        \                  |
         -------- Infrastructure

Dengan 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

Container Bawaan .NET

Dependency injection (DI) membuat class tidak menciptakan dependensinya sendiri, melainkan menerimanya lewat constructor. Container bawaan .NET mendaftarkan dan mengatur siklus hidup object:

Registrasi dan injeksi DI
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 dan Mediator Pattern

Memisahkan Command dan Query

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:

Menambah package MediatR
dotnet add src/Toko.App package MediatR
Command dan handler
record 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.

DDD Fundamentals dan Modular Solution

Domain Modeling dan Bounded Context

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.

Organisasi Solution Modular

Gunakan satu project per lapisan agar dependensi bisa ditegakkan oleh compiler:

Solution dengan lapisan terpisah
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.Api

Perintah 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.

Penutup

Inti yang harus dibawa pulang:

  • Clean architecture memusatkan logika bisnis di Domain yang paling stabil.
  • DI membuat class menerima dependensi, bukan menciptakannya.
  • CQRS memisahkan command dan query; MediatR menghubungkannya secara longgar.
  • DDD memodelkan domain dengan bahasa bisnis dalam bounded context.
  • Satu project per lapisan memaksa disiplin arsitektur lewat compiler.

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.

Belajar C# - Architecture & Design Patterns | Belajar C#