Mert Özen Her Satırda Daha İleri
Yazılım Mimarisi REST API Tasarımı — #10

API Review ve Governance: Review Checklist'i, Tasarım Kararlarını Belgelemek ve Bookstore Public API'sinin Uçtan Uca Tasarımı

Mert Özen 15 Kas 2026 13 dk 7 görüntülenme
API Review ve Governance: Review Checklist'i, Tasarım Kararlarını Belgelemek ve Bookstore Public API'sinin Uçtan Uca Tasarımı

API review, kod yazılmadan önce contract PR'ında, yazılı bir checklist'le ve danışma niteliğinde yapılmalı; araçların kontrol edebildiği her şey araca bırakılır, insan anlam, tüketici ve güvenliğe bakar. Bookstore'da bir partner taslağını lint ve review ile inceliyor, review bulgusunu yeni bir lint kuralına çeviriyor, ADR'ları ve API envanterini topluyor, satın alma onayı API'sini gerçek yanıtlarla uçtan uca tasarlıyor ve seriyi kapatıyoruz.

Sürüm notu: Bu yazıdaki kodlar C# 14 ve .NET 10 (ASP.NET Core 10.0.15) ile derlenip denenmiştir; HTTP örnekleri bu uygulamanın gerçek yanıtlarıdır, OpenAPI 3.2 dokümanı Redocly CLI 2.78.0 ve Spectral 6.17.1 ile lint edilmiştir.

Önce Tahmin Et

Sitedeki serilerin ortak örnek uygulaması Bookstore'da (bir online kitapçı) kurumsal siparişlerin bir kısmı, kurumun satın alma onaycısı onaylamadan sevk edilmemeli. Lakeside School'un entegrasyon ekibi bu iş için bir API taslağı yazıp gönderiyor; kendi testlerinden geçmiş. Taslağın bir parçası:

  /approvePurchase:
    post:
      operationId: approvePurchase
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                orderId:
                  type: string
                approved:
                  type: boolean
                approverName:
                  type: string
      responses:
        '200':
          description: success is false when the approval failed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string

Bu serinin dokuz yazısından sonra bu parçada kaç sorun görüyorsun? Bunların kaçını bir lint aracı yakalar, kaçını ancak bir insan görür? Taslağın tamamı ve review kaydı repo'da docs/reviews/2026-11-purchase-approvals/ altında.

API Review Neden ve Ne Zaman?

İlk yazıda API tasarımının geri alınması pahalı bir karar olduğunu söylemiştik: contract'ı değiştirmek, onu kullanan herkesi değiştirmek demek. Review bu pahalı kararın ucuz olduğu anda, yani kod yazılmadan önce yapılmalı. Endpoint yazılıp test edildikten sonra yapılan review'da bulunan her sorun ya iş tekrarı demek ya da "bir dahaki sürümde düzeltiriz" diye contract'a yerleşen bir hata. Bookstore'da review, contract'ı değiştiren PR (pull request; değişikliği ana branch'e almak için açılan istek) üzerinde, implementation başlamadan yapılıyor (BS-90).

Kimin review ettiği de bir karar. Büyük şirketlerde bu iş merkezi bir API ekibinin ya da bir "API guild"in elindedir. Bookstore gibi küçük bir ekipte iki kişi yetiyor: API'nin sahibi olan ekipten biri ve mümkünse bir tüketici. Lakeside School'un taslağında tüketici zaten yazarın kendisiydi; review'u Ayşe ve Emre yaptı. Önemli olan, review'un bir onay makamı değil bir danışma olması: reviewer kararı vermiyor, kararın style guide'a ve önceki kararlara uyup uymadığını gösteriyor. Uymuyorsa ve uymaması gerekiyorsa, bu bir ADR (Architecture Decision Record; mimari karar kaydı) ile kayda geçiyor. Kararların bir ekipte nasıl yaşatıldığı, architecture review ve advice process (karar vereni değil danışılacak kişileri tanımlayan süreç) Architecture Decision'lar serisinin "Kararları Yaşatmak: Decision Log, Architecture Review ve Advice Process" yazısında.

Review Checklist'i

Checklist, bu serinin dokuz yazısının özeti gibi. Her satır bir soru ve style guide'daki kurallara bağlı; araçların kontrol ettiği satırlarda reviewer araç çıktısını okuyor, gerisini kendisi kontrol ediyor:

AlanSoruKural
TüketicilerKim çağıracak: bookstore-web mi, partner mı, mobil mi? Public mi, partner mı, internal mı?ADR-0001
Resource'larDomain'den isimler, tek kanonik URL, en fazla iki path parametresi, custom method mu resource mu?BS-01…BS-07 (lint)
RepresentationcamelCase, null yerine olmayan alan, Money nesnesi, iç alan yok, response enum'ları açık mı?BS-10…BS-13, BS-52
Method ve status codeSafe ve idempotent method'lar öyle kullanılıyor mu; 201/202/204/400/409/412/422/428 rehberdeki gibi mi?BS-40…BS-46
HatalarKatalogdaki türlerle Problem Details; yeni türler katalogda mı; validation anahtarları JSON yolu mu?BS-20…BS-25 (lint)
Koleksiyonlarİlk sürümden sayfalı mı, sıralama ve filtreler yazılı mı, bilinmeyen parametre 400 mü?BS-30…BS-37 (lint)
EvrimAdditive mi? oasdiff breaking change bildiriyorsa yeni api-version ya da ADR var mı?BS-50…BS-55, BS-82 (CI)
ConcurrencyVar olanı değiştiren istek If-Match istiyor mu; yan etkili oluşturma Idempotency-Key istiyor mu?BS-60…BS-62
Uzun iş ve bulk202 ve status monitor var mı? Boyut sınırı, satır raporu, kota?BS-63…BS-76 (lint)
GüvenlikKimlik token'dan mı geliyor, body'den değil; her item için authorization var mı?BS-91, API Güvenliği
DokümantasyonAçıklamalar, status code başına gerçekçi örnekler, webhook'lar yazılı mı?BS-83
KararlarRehberden sapmalar ADR'a yazıldı mı, açık sorular listelendi mi?ADR-0001

Checklist'in uzunluğu bir tehlike: on iki satırlık bir listeyi her küçük PR'da baştan sona okumak kısa sürede formaliteye dönüşür. Bunu önlemenin yolu, kontrol edilebilen her şeyi araca bırakmak. 09. yazıdaki CI zinciri bu tablonun neredeyse yarısını reviewer'a gelmeden kontrol ediyor; insan, aracın göremediği yerlere bakıyor: anlam, tüketici, güvenlik ve tutarlılık.

Taslağın Review'u: Araç ve İnsan

Taslağı önce lint'ten geçirdik. Lint ilk çalıştığında dört hata verdi; aşağıdaki beşinci hata, review'dan sonra eklediğimiz bir kuraldan geliyor:

$ npx spectral lint docs/reviews/2026-11-purchase-approvals/draft.yaml -D

/home/user/bookstore/docs/reviews/2026-11-purchase-approvals/draft.yaml
  7:17  error  bookstore-no-verbs-in-path     Verb in path: /getApprovals                                                                          paths./getApprovals
  7:17  error  bookstore-path-kebab-case      Path is not kebab-case: /getApprovals                                                                paths./getApprovals
 22:23  error  bookstore-no-bare-arrays       #/paths/~1getApprovals/get/responses/200/content/application~1json/schema/type returns a bare array  paths./getApprovals.get.responses[200].content.application/json.schema.type
 25:20  error  bookstore-path-kebab-case      Path is not kebab-case: /approvePurchase                                                             paths./approvePurchase
 52:74  error  bookstore-max-two-path-params  Too deep: /institutions/{institutionId}/units/{unitId}/orders/{orderId}/approval                     paths./institutions/{institutionId}/units/{unitId}/orders/{orderId}/approval

✖ 5 problems (5 errors, 0 warnings, 0 infos, 0 hints)

Lint, URL'lerdeki fiilleri, camelCase'i ve üç path parametreli yolu yakaladı. Review'da ise şunlar bulundu:

  • Onaylayan kişi body'de, approverName olarak geliyor. Bunu her istemci istediği gibi doldurabilir; onaylayan, token'daki kullanıcıdır (BS-91).
  • approved: boolean iki ayrı işi tek alanda saklıyor ve reddetmenin gerekçesi yok. :approve ve gerekçe alan :reject ayrı custom method'lar olmalı (ikinci yazı).
  • Her hata success: false ile 200 dönüyor; ilk yazıdaki eski RPC (Remote Procedure Call; uzaktaki bir fonksiyonu çağırır gibi tasarlanmış API) endpoint'inin aynı hatası. 403, 404, 409, 412 ve 428, Problem Details ile dönmeli (03 ve 04).
  • İki onaycı aynı anda karar verebilir; karar bir conditional write olmalı (07).
  • getApprovals bütün onayları tek bir array'de dönüyor ve sayfalanmıyor (05). Lint bunu kaçırdı, çünkü BS-30 kuralı yalnız list ile başlayan operationId'lere bakıyordu.
  • Durum değerleri WAITING, OK, NOT_OK ve kapalı bir enum (06).
  • Onayın kendi kimliği ve yaşam döngüsü var; kanonik adresi /purchase-approvals/{approvalId}, kurum altındaki liste filtrelenmiş bir görünüm olmalı (02).
  • Partner, kararın verildiğini polling'le öğrenmek zorunda; bir webhook tanımlanmalı (08).

Beşinci madde bir kurala dönüştü: 2xx response'u çıplak bir array olan her operation artık hata alıyor, çünkü çıplak bir array'e sonradan ne sayfa token'ı ne de yeni bir alan eklenebilir. İyi bir review'un yan ürünü budur: insanın bulduğu ve tekrar edebilecek her hata, bir sonraki seferde araca bırakılır. Kural ilk çalıştığında kendi contract'ımızda da bir şey buldu:

$ npx spectral lint contract/bookstore.openapi.yaml

/home/user/bookstore/contract/bookstore.openapi.yaml
 1075:23  error  bookstore-no-bare-arrays  #/paths/~1problems/get/responses/200/content/application~1json/schema/type returns a bare array  paths./problems.get.responses[200].content.application/json.schema.type

✖ 1 problem (1 error, 0 warnings, 0 infos, 0 hints)

Hata kataloğu (GET /problems) 05. yazıdan beri sayfalanmayan, çıplak bir array dönüyor ve bunun için bir ADR'ı (ADR-0003) var. Şimdi onu bir nesneye çevirmek her istemciyi kırardı; bu yüzden kuralı o operation için kapatıp ADR-0003'e bir satır ekledik. Yeni bir kural eski kararlarla çatışabilir; çözüm kuralı gevşetmek değil, istisnayı gerekçesiyle kayda geçirmek.

Kararları Belgelemek: ADR'lar ve İstisnalar

Seri boyunca her önemli kararı bir ADR'a yazdık. Bookstore API'sinin bugünkü karar kaydı:

  • ADR-0001: Style guide, Azure rehberini temel alıyor; iki istisnası var: hatalarda RFC'lerden (Request for Comments; internet standartlarının numaralı dokümanları) 9457 numaralı Problem Details, pagination'da Google'ın AIP'lerinden (API Improvement Proposals; Google'ın numaralı API tasarım kuralları) 158 numaralı olan (01).
  • ADR-0002: Tek seferlik durum değişiklikleri custom method, yaşam döngüsü olanlar resource (02).
  • ADR-0003: Hata kataloğu sayfalanmıyor (05), bu yazıda bir istisna daha eklendi.
  • ADR-0004: Additive değişiklik sürümsüz, breaking change tarihli api-version ile; parametre opsiyonel, varsayılan en eski sürüm (06).
  • ADR-0005: Zorunlu If-Match, Idempotency-Key ve status monitor'lü uzun süren işlemler; Azure'dan POST ile başlatma sapması (07).
  • ADR-0006: Toplu sipariş, kısmi başarı, 207 yerine rapor ve RateLimit taslağı (08).
  • ADR-0007: Design-first ve CI'daki dört kontrol; araçların OpenAPI 3.2 eksikleri (09).
  • ADR-0008: Satın alma onayları ve API review süreci (bu yazı).

Bu liste, yeni bir ekip üyesinin "neden böyle?" sorusunun cevabı. Style guide kuralı söylüyor, ADR kuralın neden konduğunu ve hangi seçeneklerin reddedildiğini. Bir kuraldan sapmak serbest; sessizce sapmak değil. ADR yazmanın kendisi, şablonları ve bir ADR'ın ne kadar ayrıntılı olacağı Architecture Decision'lar serisinin "ADR Yazmak" yazısında.

API Envanteri

Governance'ın en çok ihmal edilen parçası, hangi API'lerin var olduğunu bilmek. Bookstore'da docs/api-catalog.md her API yüzeyini bir satırda tutuyor: hedef kitlesi (public, bookstore-web, partner), sahibi olan ekip, contract'ı ve yaşam döngüsü durumu. Eski RPC endpoint'i de orada: deprecated, sunset tarihi 31 Mart 2027, kullanımı istemci başına log'lanıyor. 09. yazıdaki drift kontrolü bu listenin güncel kalmasını sağlıyor: koda contract'ta olmayan bir endpoint eklenirse CI kırılıyor. Bir envanterin güvenlik tarafı, yani unutulmuş ve dokümante edilmemiş endpoint'lerin saldırganlar için neden değerli olduğu, API Güvenliği serisinin yanlış yapılandırma ve envanter yazısında anlatılıyor.

Uçtan Uca: Bookstore'un Partner API'si

Review'dan sonra tasarım şu hâle geldi (ADR-0008). Her bekleyen kurumsal sipariş için bir satın alma onayı oluşuyor. Kanonik adres /purchase-approvals/{approvalId}; /institutions/{institutionId}/purchase-approvals sayfalı ve status ile filtrelenen bir görünüm. Karar iki custom method: :approve ve gerekçe isteyen :reject. Yalnız kurumun onaycısı karar verebiliyor, karar If-Match istiyor ve karara bağlanmış bir onay ikinci kez karara bağlanamıyor. Önce contract yazıldı, lint ve review'dan geçti, Prism mock'u Lakeside'ın ekibine verildi; kod ondan sonra yazıldı. Gerçek API'de akış:

$ curl -s -H "Authorization: Bearer linus" "http://127.0.0.1:52318/institutions/lakeside-school/purchase-approvals?status=Pending" | jq -c '.items[] | {id, orderId, unitId, amount: .amount.amount, status}'
{"id":"01a1c112-d7c0-7a06-81ce-e75908702949","orderId":"01a1c112-d7c0-706c-8cb9-c18a8fadc1a6","unitId":"main-campus","amount":7884.00,"status":"Pending"}

$ APPROVAL=$(curl -s -H "Authorization: Bearer linus" "http://127.0.0.1:52318/institutions/lakeside-school/purchase-approvals?status=Pending" | jq -r '.items[0].id')

$ curl -s -i -H "Authorization: Bearer linus" http://127.0.0.1:52318/purchase-approvals/$APPROVAL | grep -E '^(HTTP|ETag)'
HTTP/1.1 200 OK
ETag: "1"

$ curl -s -X POST -H "Authorization: Bearer nevin" -H 'If-Match: "1"' "http://127.0.0.1:52318/purchase-approvals/${APPROVAL}:approve" | jq -c '{status, type, detail}'
{"status":403,"type":"https://api.example.com/problems/approver-required","detail":"Only an approver of the institution can decide on a purchase approval."}

$ curl -s -X POST -H "Authorization: Bearer linus" "http://127.0.0.1:52318/purchase-approvals/${APPROVAL}:approve" | jq -c '{status, type}'
{"status":428,"type":"https://api.example.com/problems/precondition-required"}

$ curl -s -i -X POST -H "Authorization: Bearer linus" -H 'If-Match: "1"' "http://127.0.0.1:52318/purchase-approvals/${APPROVAL}:approve" | grep -E '^(HTTP|ETag)|^\{'
HTTP/1.1 200 OK
ETag: "2"
{"id":"01a1c112-d7c0-7a06-81ce-e75908702949","orderId":"01a1c112-d7c0-706c-8cb9-c18a8fadc1a6","institutionId":"lakeside-school","unitId":"main-campus","amount":{"amount":7884.00,"currency":"TRY"},"status":"Approved","requestedAt":"2026-11-09T16:10:00Z","decidedBy":"linus","decidedAt":"2026-11-14T23:45:20.599698Z"}

$ curl -s -H "Authorization: Bearer linus" http://127.0.0.1:52318/orders/01a1c112-d7c0-706c-8cb9-c18a8fadc1a6 | jq -c '{id, status}'
{"id":"01a1c112-d7c0-706c-8cb9-c18a8fadc1a6","status":"Confirmed"}

$ curl -s -X POST -H "Authorization: Bearer linus" -H 'If-Match: "2"' "http://127.0.0.1:52318/purchase-approvals/${APPROVAL}:approve" | jq -c '{status, type, detail}'
{"status":409,"type":"https://api.example.com/problems/approval-already-decided","detail":"Purchase approval 01a1c112-d7c0-7a06-81ce-e75908702949 is already Approved."}

Nevin Hanım onayı görebiliyor, çünkü kendi biriminin siparişi; bu yüzden karar vermeye çalıştığında 404 değil 403 alıyor (BS-45). Linus'un header'sız isteği 428, doğru ETag'li isteği 200 alıyor ve sipariş Confirmed'a geçiyor. Karara bağlanmış onaya ikinci karar 409. Reddetme de aynı şekilde çalışıyor, gerekçeyle:

$ curl -s -X POST -H "Authorization: Bearer nevin" -H "Content-Type: application/json" -H 'Idempotency-Key: "lakeside-2026-11-extra"' -d '{"unitId":"main-campus","lines":[{"isbn":"9780553293357","quantity":6}]}' http://127.0.0.1:52318/institutions/lakeside-school/order-batches | jq -c '{orderId, acceptedLines}'
{"orderId":"01a1dc73-aec0-71e7-a2f9-67755d94f47d","acceptedLines":1}

$ APPROVAL2=$(curl -s -H "Authorization: Bearer linus" "http://127.0.0.1:52318/institutions/lakeside-school/purchase-approvals?status=Pending" | jq -r '.items[0].id')

$ curl -s -X POST -H "Authorization: Bearer linus" -H 'If-Match: "1"' -H "Content-Type: application/json" -d '{"reason":"Foundation is already in the east campus library."}' "http://127.0.0.1:52318/purchase-approvals/${APPROVAL2}:reject" | jq -c '{status, decidedBy, reason}'
{"status":"Rejected","decidedBy":"linus","reason":"Foundation is already in the east campus library."}

$ curl -s -H "Authorization: Bearer nevin" "http://127.0.0.1:52318/orders/$(curl -s -H "Authorization: Bearer nevin" http://127.0.0.1:52318/purchase-approvals/$APPROVAL2 | jq -r .orderId)" | jq -c '{status, total: .total.amount}'
{"status":"Cancelled","total":1614.60}

Bu küçük akış serinin neredeyse bütün kararlarını bir arada kullanıyor: domain'den gelen bir resource ve custom method'lar, katalogdaki problem türleri, sayfalı ve filtreli bir liste, açık bir durum listesi, conditional write, Idempotency-Key'li toplu sipariş ve contract'ta tanımlı, planlanmış bir purchaseApprovalDecided webhook'u. Review kaydına yazılan açık kararlar da tasarımın parçası: belirli bir tutarın üstünde iki onaycı gerekip gerekmeyeceği (Release 2), bir onayın süresinin dolup dolmayacağı ve alıcının bekleyen bir isteği geri çekip çekemeyeceği. Bunları şimdi karara bağlamak zorunda değiliz, ama yazılı olmaları gelecekteki değişikliğin additive mi breaking mi olacağını şimdiden düşündürüyor. Örneğin geri çekme bir custom method olarak sonradan eklenebilir; onay durum listesi açık olduğu için yeni bir Withdrawn değeri de additive bir değişiklik.

ASP.NET Core'da

Onay endpoint'leri serinin geri kalanıyla aynı parçalardan oluşuyor: route group'lar, TypedResults, katalogdan ProblemCatalog.Create, 05. yazının Paging sınıfı, bind edilmiş body için .NET 10'un AddValidation'ı. Kararın kendisi tek bir kilit altında üç kontrol yapıyor (If-Match var mı, eşleşiyor mu, onay hâlâ bekliyor mu) ve siparişin durumunu aynı anda değiştiriyor; veritabanlı bir uygulamada bu, tek bir transaction ve rowversion ile yapılır. Rol kontrolü bilerek endpoint'in içinde: "onayı görebilen ama karar veremeyen" kullanıcıya 404 değil 403 dönmek, yalnız policy ile değil resource'a bakarak verilebilecek bir karar. Bu tür resource-based authorization ASP.NET Core Web API serisinin "ASP.NET Core Web API'de Authorization: Roller, Policy'ler ve Resource-Based Yetkilendirme" yazısında.

Sık Düşülen Tuzaklar

Governance'ı onay bürokrasisine çevirmek. Her PR'ın bir komiteyi beklemesi, ekiplerin review'dan kaçmasına yol açar: ya API'yi contract'a yazmazlar ya da değişikliği küçük parçalara bölüp gözden kaçırırlar. Review hızlı, yazılı kurallara dayalı ve danışma niteliğinde olmalı.

Review'u implementation bittikten sonra yapmak. Kod bitmişken bulunan bir tasarım hatası "sonra düzeltiriz" diye contract'a girer ve bir sonraki sürüme kadar orada kalır.

Checklist'i araçla kontrol edilebilecek şeylerle doldurmak. Reviewer'ın dikkati, aracın göremediği anlam ve güvenlik sorularına kalmalı. Review'da tekrar tekrar bulunan bir hata bir lint kuralı olmalı.

İstisnaları sessizce kabul etmek. Gerekçesi yazılmamış her istisna, bir sonraki ekibin "burada da böyle yapılmış" diye kopyaladığı bir örnek olur.

Envanteri bir kez yazıp bırakmak. Drift kontrolü olmayan bir envanter, ilk acil düzeltmede gerçekle bağını kaybeder.

Seri Boyunca Ne Yaptık ve Buradan Nereye?

On yazı boyunca Bookstore'un API'sini kod yazmadan önce verilen kararlar üzerinden tasarladık: contract'ın neden bir mimari karar olduğu ve kendi style guide'ımız, resource'lar ve URL'ler, method ve status code'lar, RFC 9457 hata contract'ı, koleksiyonlar, versioning ve deprecation, concurrency, idempotency ve uzun süren işlemler, toplu işlemler ve kota, design-first akış ve son olarak review ile governance. Her kararın arkasında bir style guide kuralı, çoğunun arkasında bir ADR, hepsinin arkasında gerçek bir HTTP yanıtı var; snapshot'lar her yazının sonundaki kodun tamamını içeriyor.

Bu seri tasarımı anlattı ve implementation ayrıntısını bilerek kısa tuttu. Kodun derinine inmek için iki seri var. ASP.NET Core Web API serisi aynı kararların framework tarafını anlatıyor; özellikle son yazıları bu serinin yanında okunmalı: "HttpContext, HttpRequest ve HttpResponse ile Çalışmak: Header, Cookie, Query, Body, Items ve Feature'lar", Minimal API'yi derinlemesine anlatan iki yazı (parameter binding ve route group'lar, endpoint filter'lar, TypedResults), "JSON, Content Negotiation ve Formatter'lar", "Built-in Middleware Kataloğu" ve "Web API'de Localization: Accept-Language, RequestLocalization, Yerelleştirilmiş Validation ve ProblemDetails Mesajları". Çözüm Desenleri serisi ise contract'ın arkasındaki veritabanı kararlarını anlatıyor: "Id'yi Dışarıya Açmak", "Para ve Yuvarlama", "Zaman ve Time Zone", "Idempotency Key", "Optimistic Concurrency Uçtan Uca", "Pagination Stratejileri Derinlemesine" ve "Toplu Import ve Validation Raporu". Bu serideki bir contract kararının veritabanında ve kodda nasıl karşılık bulduğunu merak ettiğinde, iki serinin ilgili yazısı yan yana okunabilir.

Küçük Bir Deneme

★☆☆ Checklist'i kullanarak 07. yazıdaki fatura endpoint'ini review et. Hangi satırlar araçla kontrol edildi, hangilerine sen baktın? Bulduğun bir sorun var mı?

★★☆ Onay için "geri çekme" özelliğini tasarla: :withdraw custom method'u, kimin çağırabileceği, hangi durumlarda 409 döneceği ve yeni durum değeri. Değişikliği contract'a yaz, lint'ten ve oasdiff'ten geçir. oasdiff bunu breaking sayıyor mu?

★★★ Belirli bir tutarın üstündeki siparişler için iki onaycı gereksin. Bunu mevcut contract'ı kırmadan nasıl modellersin: onay nesnesine bir steps listesi mi, ayrı bir resource mu? Kararını ve reddettiğin seçenekleri ADR-0009 olarak yaz.

Bu yazıyla seri bitti. Bir API'yi tasarlarken bu serinin sırasını bir kontrol listesi gibi kullanabilirsin: önce tüketiciyi ve contract'ı düşün, sonra resource'ları, method'ları, hataları ve koleksiyonları; evrimi, eşzamanlılığı ve uzun süren işleri baştan contract'a yaz; ve hepsini bir araç zinciriyle ve kısa, yazılı bir review ile koru. Kodun ayrıntısı için ASP.NET Core Web API ve Çözüm Desenleri serileri seni bekliyor.