Mert Özen Her Satırda Daha İleri
Yazılım Tasarımı UML ve Modeling — #14

C4 Model: Context, Container, Component, Code ve Notation Kuralları

Mert Özen 7 Kas 2026 11 dk 3 görüntülenme
C4 Model: Context, Container, Component, Code ve Notation Kuralları

C4 model, bir yazılım sistemini Context, Container, Component ve Code seviyelerinde, her biri ayrı bir okuyucuya hitap eden yakınlaşan haritalarla anlatır; çoğu ekip için ilk iki seviye yeterlidir ve C4'teki container bir Docker container değil, çalışan bir uygulama ya da veri deposudur. Bu yazıda İK Portalı'nın Context, Container ve Component diyagramlarını notation checklist'ine uygun çizip C4-PlantUML ile koda döküyor ve UML ile nasıl birlikte kullanılacağını gösteriyoruz.

Sürüm notu: Bu yazıdaki diyagramlar C4 (Context, Containers, Components, Code) model ve UML (Unified Modeling Language) 2.5.1 notasyonuyla çizilmiştir; PlantUML örneği PlantUML 1.2026.9 ve C4-PlantUML 2.14.0, Mermaid örneği Mermaid 12.3.0 ile render edilmiştir; serinin kod tabanı .NET 10'dur.

Önce Tahmin Et: İK Portalı'nda Kaç Container Var?

Ekibe yeni katılan bir geliştirici basit bir soru soruyor: "İK (insan kaynakları) Portalı kaç container'dan oluşuyor?" Üç kişi üç cevap veriyor. Geliştirici Ada, bir önceki yazıdaki compose dosyasını gösteriyor:

sqlserver
servicebus
worker
redis
api

"Beş." Ops'tan Linus "production'da iki container app var, API (Application Programming Interface) ve worker" diyor. Mimar Grace ise altı sayıyor ve listesinde compose'da hiç olmayan Angular uygulaması da var. Hangisi doğru? Soru yanlış mı sorulmuş, yoksa "container" kelimesi üç kişi için üç farklı şey mi ifade ediyor? Cevabı vermeden önce Grace'in listesindeki altıncı öğenin ne olabileceğini tahmin et.

C4 Nedir? Dört Seviye ve Hedef Kitleleri

C4 model, Simon Brown'ın yazılım mimarisini birbirine yakınlaşan dört seviyede anlatmak için önerdiği bir yaklaşım. Kaynakları c4model.com sitesi ve 2026'da O'Reilly'den çıkan The C4 Model kitabı. Fikir haritacılıktan geliyor: önce ülkeyi, sonra şehri, sonra mahalleyi ve en son sokağı gösterirsin; her harita bir öncekine yakınlaşır ve her birinin okuyucusu farklıdır. C4 bir notasyon değil, bir soyutlama seti: hangi kutunun ne anlama geldiğini tanımlar, kutunun şeklini ve rengini sana bırakır.

SeviyeKapsamHedef kitleÖneri
1. System ContextTek yazılım sistemi ve çevresiTeknik olan ve olmayan herkesHer ekip için
2. ContainerBir sistemin içindeki çalışan uygulamalar ve veri depolarıMimarlar, geliştiriciler, opsHer ekip için
3. ComponentTek bir container'ın içiMimarlar ve geliştiricilerDeğer katıyorsa; uzun ömürlü dokümanda otomatik üretilmeli
4. CodeTek bir component'in class'larıGeliştiricilerÖnerilmez; gerekirse araçla üretilir

Tablonun son sütunu C4'ün en çok gözden kaçan mesajı: dört seviyenin hepsini çizmek zorunda değilsin. c4model.com, Context ve Container diyagramlarının çoğu ekip için yeterli olduğunu açıkça söylüyor. Component seviyesi yalnız gerçekten fayda sağlıyorsa çizilmeli, Code seviyesi ise neredeyse hiç elle çizilmemeli.

"Container" Docker Container Değildir

Açılıştaki karışıklığın kaynağı bu. C4'te container, kod çalıştıran ya da veri saklayan bir çalışma zamanı sınırıdır: sistemin çalışması için ayakta olması gereken, ayrı deploy edilen bir uygulama ya da veri deposu. Sunucu tarafı web uygulaması, tarayıcıda çalışan bir SPA (single-page application), bir console uygulaması, bir serverless function, bir veritabanı şeması ya da bir blob store container'dır. c4model.com bu sayfaya bilerek "Not Docker!" uyarısıyla başlıyor. İsim, Docker'dan önce seçilmiş genel bir kelime.

Bu tanımla Grace'in altı container'ı şunlar: Angular SPA, API, worker, veritabanı, cache ve leave-events topic'i. Altıncı öğe Angular uygulaması. Docker'da çalışmıyor ama kullanıcının tarayıcısında çalışan ayrı bir uygulama. Ada'nın listesindeki sqlserver, redis ve servicebus ise geliştirme ortamında bu container'ların hangi Docker container'larında çalıştığını gösteriyor, yani deployment bilgisi. Linus'un iki container app'i de aynı şekilde production'daki deployment. Üç cevap da kendi sorusu için doğru; C4'ün sağladığı şey, bu üç sorunun ayrı diyagramlara ait olduğunu adlandırması.

İki sınır vakası daha var. Bir DLL (dynamic-link library) ya da NuGet paketi container değildir, çünkü tek başına çalışmaz; kodu organize eder. Önceki yazılardaki HrPortal.Leave.dll bu yüzden C4'te en fazla bir component'in fiziksel karşılığıdır. Message queue ve topic'ler için C4 iki yol önerir: her queue ya da topic'i ayrı bir container (veri deposu) olarak çizmek ya da yalnız ilişki etiketine "via leave-events" yazıp queue'yu gizlemek. Broker'ın kendisi (Service Bus namespace'i) ise container değildir. Biz leave-events'i açık container olarak çizdik, çünkü API ile worker arasındaki bağın bir topic üzerinden geçtiğini görmek mimari açıdan önemli.

Seviye 1: System Context

14-c4-context-tr.png

Context diyagramının merkezinde tek bir kutu var: İK Portalı. Etrafında onu kullanan insanlar ve onun konuştuğu dış sistemler duruyor. Teknoloji, protokol ya da iç yapı yok; bu diyagramı İK direktörü de, bilgi güvenliği ekibi de okuyabilmeli. Kişiler, use case diagram'daki actor'lerle aynı: Çalışan, Yönetici (direktör de bir yönetici) ve İK Uzmanı. Dış sistemler ise Kurumsal Takvim, e-posta sistemi ve kimlik sağlayıcı.

Bu diyagramın asıl işi sınırı çizmek. Bordro bizim sistemimizin içinde, takvim değil. Kimlik doğrulama başkasının sorumluluğunda, e-posta teslimi de öyle. Bir güvenlik incelemesinde ya da bir entegrasyon toplantısında ilk sorulan soru "sizin sistem nerede bitiyor?" sorusudur ve cevabı bu tek sayfa verir. Dikkat edersen her ok tek yönlü ve üzerinde ne için olduğu yazıyor; "kullanır" gibi tek kelimelik belirsiz etiketler yok.

Seviye 2: Container

14-c4-container-tr.png

Container diyagramı İK Portalı kutusunu açıyor. Kesikli sınırın içindeki altı container, açılıştaki sorunun cevabı. Her kutunun adı, tipi, teknolojisi ve tek cümlelik sorumluluğu var. Her ok tek yönlü ve köşeli parantez içinde teknolojisini taşıyor: [JSON/HTTPS], [EF Core, TDS], [AMQP]. Container'lar arası ilişkiler genelde process'ler arası iletişim olduğu için teknoloji etiketi burada zorunlu sayılır.

Diyagramda olmayan şeyler de bilinçli. Replica sayısı, Azure bölgesi, Front Door, port numaraları yok. Bunlar ortamdan ortama değişir ve bir önceki yazıda deployment diagram'da duruyorlar. Container diyagramı geliştirme ortamında da production'da da aynı kalır; değişen yalnız container'ların nerede çalıştığıdır. Aynı modelin diagrams-as-code karşılığı aşağıda, C4-PlantUML 2.14.0 ile yazıldı:

@startuml hr-portal-containers
!include <C4/C4_Container>

title Container diagram for HR Portal

Person(employee, "Employee", "Requests leave and checks the balance")
Person(manager, "Manager", "Approves or rejects leave requests; a director is a manager")
Person(hr, "HR Specialist", "Manages leave types, holidays and balances")
System_Ext(calendar, "Corporate Calendar System", "Shows approved leave in the company calendar")
System_Ext(smtp, "E-mail System", "Delivers notification e-mails")
System_Ext(idp, "Identity Provider", "Microsoft Entra ID")

System_Boundary(hrportal, "HR Portal") {
    Container(spa, "Single-Page Application", "Angular 22", "Leave request, approval and balance screens")
    Container(api, "API Application", "ASP.NET Core, .NET 10", "Leave, Payroll and Notification modules")
    Container(worker, "Worker", ".NET 10 Worker Service", "Sends notifications and approval reminders")
    ContainerDb(db, "Database", "Azure SQL Database", "Leave requests, approvals, balances")
    ContainerDb(cache, "Cache", "Azure Managed Redis", "Balances and lookup data")
    ContainerQueue(topic, "leave-events", "Azure Service Bus topic", "Leave request integration events")
}

Rel(employee, spa, "Creates and cancels leave requests", "HTTPS")
Rel(manager, spa, "Approves or rejects requests", "HTTPS")
Rel(hr, spa, "Maintains leave rules and balances", "HTTPS")
Rel(spa, idp, "Signs the user in", "OpenID Connect")
Rel(spa, api, "Reads and writes leave data", "JSON/HTTPS")
Rel(api, db, "Reads and writes", "EF Core, TDS")
Rel(api, cache, "Caches balances", "RESP/TLS")
Rel(api, topic, "Publishes LeaveRequestApproved etc.", "AMQP")
Rel(api, calendar, "Adds and removes leave entries", "JSON/HTTPS")
Rel(worker, topic, "Consumes events from", "AMQP")
Rel(worker, db, "Reads pending requests", "TDS")
Rel(worker, smtp, "Sends e-mails via", "SMTP")

SHOW_LEGEND()
@enduml

Container, ContainerDb ve ContainerQueue makroları şekli seçiyor, Rel'in dördüncü argümanı teknoloji etiketi, SHOW_LEGEND() ise checklist'in istediği legend'ı otomatik üretiyor. Mermaid 12.3.0'ın da C4 diyagram desteği var ve sözdizimi C4-PlantUML'e bilerek benzetilmiş:

C4Context
    title System Context diagram for HR Portal
    Person(employee, "Employee", "Requests leave and checks the balance")
    Person(manager, "Manager", "Approves or rejects leave requests")
    System(hrportal, "HR Portal", "Leave requests, approvals, balances and payroll")
    System_Ext(calendar, "Corporate Calendar System", "Shows approved leave")
    Rel(employee, hrportal, "Requests and cancels leave")
    Rel(manager, hrportal, "Approves or rejects requests")
    Rel(hrportal, calendar, "Writes approved leave, removes it on cancel", "JSON/HTTPS")

Bu metin Mermaid 12.3.0 ile render ediliyor, ama iki sınırı hemen görünüyor. Mermaid dokümantasyonuna göre yerleşim otomatik değil, kutuların yeri komut sırasına göre belirleniyor; bizim denememizde takvim ilişkisinin etiketi kutunun üzerine bindi. Legend desteği de henüz yok. Hızlı bir taslak için yeterli, notation checklist'ine uyan bir doküman için C4-PlantUML ya da Structurizr daha uygun. Üç aracın karşılaştırmasını serinin Mermaid ve Structurizr DSL (domain-specific language) yazısında yapıyoruz.

Seviye 3: Component ve Seviye 4: Code

14-c4-component-tr.png

Component diyagramı tek bir container'ı açar; burada API Application. C4'te component, iyi tanımlanmış bir interface'in arkasında toplanmış ilgili işlevselliktir. Ayrı deploy edilmez, container'ın process'inde çalışır. C# için en basit zihinsel model, bir interface ve onu gerçekleştiren class'lar. Bizim component'lerimiz Component Diagram yazısındaki modüllerin aynısı: Leave, Payroll, Notification ve Calendar Integration. Bunlara HTTP (Hypertext Transfer Protocol) isteklerini karşılayan endpoint'ler ve veri erişimi ekleniyor. Her kutuda interface ya da class adı yazıyor, böylece diyagramdan koda giden yol tek adım.

UML component diagram'ıyla farkı burada somutlaşıyor. Aynı Leave ve Payroll ilişkisini UML'de lollipop, socket ve port'larla çizmiştik; C4'te ise tek bir etiketli ok var: "Ücretsiz günleri sorar [IApprovedLeaveQuery]". C4 diyagramı okuyucuya "ne var ve ne konuşuyor" bilgisini verir; UML ise sözleşmenin yönünü ve sahipliğini kesinleştirir.

Dördüncü seviye Code, yani bir component'in class diagram'ı. c4model.com bunu açıkça önermiyor. Gerekiyorsa IDE (integrated development environment) ya da bir modelleme aracı üretmeli. Elle çizilen class diagram ilk refactoring'de bayatlar. Koddan diyagram üretmeyi serinin Class Diagram'dan Koda ve Koddan Diyagrama: Domain Model, Design Model ve puml-gen yazısında işlemiştik. C4'ün dördüncü seviyesi için yapılacak şey, o yazıdaki puml-gen çıktısını ilgili component'e bağlamaktan ibaret.

Notation Checklist

C4 şekil ve renk dayatmıyor ama diyagramın kendi kendini açıklamasını şart koşuyor. c4model.com'daki notation checklist'inin maddeleri ve bizim diyagramlarımızın onlara nasıl uyduğu:

  • Başlık diyagramın tipini ve kapsamını söyler: "[Container] İK Portalı" gibi.
  • Her diyagramda şekilleri, renkleri, çizgi türlerini ve ok uçlarını açıklayan bir legend vardır; UML ile çizilse bile.
  • İş ya da teknik kısaltmalar her okuyucu için anlaşılır olur ya da legend'da açıklanır.
  • Her elemanın tipi yazar ([Person], [Software System], [Container], [Component]) ve kısa bir sorumluluk cümlesi taşır.
  • Her container ve component'in teknolojisi belirtilir.
  • Her çizgi tek yönlüdür ve etiketi yönüyle uyumludur; "kullanır" gibi belirsiz tek kelimeler yerine amacı söyler.
  • Container'lar arası ilişkiler teknoloji ya da protokol etiketi taşır; renkler renk körlüğü ve siyah-beyaz baskı düşünülerek seçilir.

Son madde bizim figürlerimizde de bir karar gerektirdi: kişi, sistem ve container'ı yalnız renkle değil, kenar stiliyle de ayırdık. Dış sistemler kesikli kenarlı, kişiler insan ikonlu. Böylece siyah-beyaz bir çıktıda ya da renk ayırt edemeyen bir okuyucuda da ayrım kaybolmuyor.

Tamamlayıcı Diyagramlar: System Landscape, Dynamic ve Deployment

Dört çekirdek seviyenin yanında C4 üç tamamlayıcı diyagram tanımlar. System landscape, şirketteki bütün yazılım sistemlerini ve aralarındaki ilişkileri tek resimde gösterir. İK Portalı, bordro ödemesini yapan muhasebe sistemi, kurumsal takvim ve kimlik sağlayıcı aynı haritada görünür. Bir kurumda birden fazla ekip birden fazla sistem yönetiyorsa context diyagramlarının üstüne çıkan ilk diyagram budur.

Dynamic diagram, statik bir diyagramdaki elemanların belirli bir senaryoda nasıl iş birliği yaptığını numaralı oklarla gösterir; örneğin "izin onaylanınca" akışı: SPA, API'yi çağırır, API veritabanına yazar ve topic'e event yayınlar, worker event'i alıp e-posta gönderir. Bu, UML sequence diagram'ın daha hafif bir kardeşi. Mesaj türlerini, fragment'leri ve hata yollarını göstermen gerekiyorsa serinin Sequence Diagram yazılarına dönersin. Deployment diagram ise bir önceki yazıda gördüğümüz UML deployment diagram'ından türetilmiş. Deployment node'ları iç içe çizilir ve içlerine container instance'ları konur. Ortam başına bir tane çizilir.

UML ile C4'ü Birlikte Kullanmak

Bu seride ikisini rakip değil, katman olarak kullanıyoruz. C4 iskeleti verir: Context ve Container diyagramları mimari dokümanın ilk iki sayfasıdır ve sistemi bilmeyen birine on dakikada yön gösterir. UML ise iskeletin kritik noktalarında detayı verir. İzin talebinin yaşam döngüsü bir state machine diagram, modül sınırı bir UML component diagram, onay akışının hata yolları bir sequence diagram, production topolojisi bir deployment diagram ile anlatılır. Pratik kural: okuyucu "ne var ve ne ile konuşuyor?" diye soruyorsa C4, "tam olarak nasıl davranıyor ya da hangi sözleşmeyle bağlı?" diye soruyorsa UML.

C4'ün bir de kalıcı dokümantasyondaki rolü var. Bir mimari karar alındığında, örneğin "Bordro, İzin modülünü sorgulamak yerine event dinlesin", kararın hangi container ve component'leri etkilediği bu diyagramlar üzerinden gösterilir. Kararın kendisini ADR (Architecture Decision Record) olarak yazıp diyagramı ona eklemeyi ilerideki Architecture Decision'lar serisinde yapacağız. O seri C4 notasyonunu yeniden anlatmayacak, bu yazıya dayanacak.

Sık Düşülen Tuzaklar

Etiketsiz kutu-ok diyagramı. Beyaz tahtadaki klasik resim: altı kutu, on ok, hiçbir okta ne taşındığı yazmıyor, hiçbir kutuda teknolojisi yok. Okuyan herkes farklı bir şey anlar. Checklist'in neredeyse tamamı bu tek tuzağa karşı yazılmış.

Container'ı Docker container sanmak. Container diyagramına sqlserver, redis ve servicebus Docker container'larını çizip Angular uygulamasını unutmak, bir deployment diyagramını yanlış seviyede çizmek demek. Önce "sistemin çalışması için ayakta olması gereken ne var?" diye sor, sonra "nerede çalışıyor?" sorusunu deployment'a bırak.

Code seviyesini elle çizmek. Her component için elle çizilen class diagram'lar, bakımı en pahalı ve en hızlı bayatlayan dokümandır. Gerekirse üret, gerekmiyorsa çizme.

Seviyeleri tek diyagramda karıştırmak. Context diyagramına veritabanı, container diyagramına class adı koymak, okuyucunun zihnindeki haritayı bozar. Her diyagram tek bir kapsam ve tek bir soyutlama seviyesinde kalmalı. Yakınlaşmak istiyorsan bir sonraki seviyenin diyagramını çiz.

İki yönlü ve belirsiz oklar. "API ↔ Veritabanı: kullanır" bir şey söylemez. "API, talepleri okur ve yazar [EF Core, TDS]" ise hem yönü hem amacı hem teknolojiyi söyler.

Küçük Bir Deneme

★☆☆ İK Portalı'nın bordro ödemesini yapan bir Muhasebe Sistemi'ne aylık ücretsiz izin raporu gönderdiğini varsay. Context diyagramına ne eklenir? Etiketi checklist'e uygun biçimde yaz.

★★☆ "İzin onayı" senaryosu için bir C4 dynamic diagram çiz: numaralı adımlarla SPA, API, Database, leave-events, Worker ve E-posta Sistemi. Sonra aynı senaryonun serideki sequence diagram'ıyla karşılaştır: dynamic diagram neyi göstermiyor?

★★★ Container diyagramının C4-PlantUML kaynağını repo'ya koy ve leave-events topic'ini gizleyen alternatif bir sürüm yaz: API ile Worker arasında "via leave-events" etiketli tek bir ilişki. İki sürümü bir ekip arkadaşına göster ve hangisinin mimariyi daha doğru anlattığını tartışın. C4'ün "explicit" ve "implicit" queue seçeneklerinden hangisini seçtiniz, neden?

Şimdiye kadar davranış ve mimari diyagramlarına baktık. Bir sonraki yazıda verinin kendisine iniyoruz: ER (Entity-Relationship) diagram ile Chen ve Crow's Foot notasyonlarını, cardinality ve optionality'yi okumayı ve İzin modülünün class diagram'ından tablolara geçişi göreceğiz.